mirror of
https://github.com/netbirdio/netbird.git
synced 2025-01-26 07:48:48 +01:00
Merge branch 'main' into feature/mysql-support
# Conflicts: # management/server/account.go # management/server/store/sql_store.go # management/server/store/store.go
This commit is contained in:
commit
214b863e40
4
.github/workflows/golang-test-linux.yml
vendored
4
.github/workflows/golang-test-linux.yml
vendored
@ -183,7 +183,7 @@ jobs:
|
|||||||
run: git --no-pager diff --exit-code
|
run: git --no-pager diff --exit-code
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} NETBIRD_STORE_ENGINE=${{ matrix.store }} CI=true go test -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' -timeout 10m $(go list ./... | grep /management)
|
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} NETBIRD_STORE_ENGINE=${{ matrix.store }} CI=true go test -p 1 -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' -timeout 10m $(go list ./... | grep /management)
|
||||||
|
|
||||||
benchmark:
|
benchmark:
|
||||||
needs: [ build-cache ]
|
needs: [ build-cache ]
|
||||||
@ -232,7 +232,7 @@ jobs:
|
|||||||
run: git --no-pager diff --exit-code
|
run: git --no-pager diff --exit-code
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} NETBIRD_STORE_ENGINE=${{ matrix.store }} CI=true go test -run=^$ -bench=. -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' -timeout 10m ./...
|
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} NETBIRD_STORE_ENGINE=${{ matrix.store }} CI=true go test -run=^$ -bench=. -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' -timeout 10m -p 1 ./...
|
||||||
|
|
||||||
test_client_on_docker:
|
test_client_on_docker:
|
||||||
needs: [ build-cache ]
|
needs: [ build-cache ]
|
||||||
|
4
.github/workflows/golangci-lint.yml
vendored
4
.github/workflows/golangci-lint.yml
vendored
@ -46,7 +46,7 @@ jobs:
|
|||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev libpcap-dev
|
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev libpcap-dev
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v3
|
uses: golangci/golangci-lint-action@v4
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
args: --timeout=12m
|
args: --timeout=12m --out-format colored-line-number
|
||||||
|
@ -21,6 +21,8 @@ type Anonymizer struct {
|
|||||||
currentAnonIPv6 netip.Addr
|
currentAnonIPv6 netip.Addr
|
||||||
startAnonIPv4 netip.Addr
|
startAnonIPv4 netip.Addr
|
||||||
startAnonIPv6 netip.Addr
|
startAnonIPv6 netip.Addr
|
||||||
|
|
||||||
|
domainKeyRegex *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultAddresses() (netip.Addr, netip.Addr) {
|
func DefaultAddresses() (netip.Addr, netip.Addr) {
|
||||||
@ -36,6 +38,8 @@ func NewAnonymizer(startIPv4, startIPv6 netip.Addr) *Anonymizer {
|
|||||||
currentAnonIPv6: startIPv6,
|
currentAnonIPv6: startIPv6,
|
||||||
startAnonIPv4: startIPv4,
|
startAnonIPv4: startIPv4,
|
||||||
startAnonIPv6: startIPv6,
|
startAnonIPv6: startIPv6,
|
||||||
|
|
||||||
|
domainKeyRegex: regexp.MustCompile(`\bdomain=([^\s,:"]+)`),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,20 +175,15 @@ func (a *Anonymizer) AnonymizeSchemeURI(text string) string {
|
|||||||
return re.ReplaceAllStringFunc(text, a.AnonymizeURI)
|
return re.ReplaceAllStringFunc(text, a.AnonymizeURI)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AnonymizeDNSLogLine anonymizes domain names in DNS log entries by replacing them with a random string.
|
|
||||||
func (a *Anonymizer) AnonymizeDNSLogLine(logEntry string) string {
|
func (a *Anonymizer) AnonymizeDNSLogLine(logEntry string) string {
|
||||||
domainPattern := `dns\.Question{Name:"([^"]+)",`
|
return a.domainKeyRegex.ReplaceAllStringFunc(logEntry, func(match string) string {
|
||||||
domainRegex := regexp.MustCompile(domainPattern)
|
parts := strings.SplitN(match, "=", 2)
|
||||||
|
|
||||||
return domainRegex.ReplaceAllStringFunc(logEntry, func(match string) string {
|
|
||||||
parts := strings.Split(match, `"`)
|
|
||||||
if len(parts) >= 2 {
|
if len(parts) >= 2 {
|
||||||
domain := parts[1]
|
domain := parts[1]
|
||||||
if strings.HasSuffix(domain, anonTLD) {
|
if strings.HasSuffix(domain, anonTLD) {
|
||||||
return match
|
return match
|
||||||
}
|
}
|
||||||
randomDomain := generateRandomString(10) + anonTLD
|
return "domain=" + a.AnonymizeDomain(domain)
|
||||||
return strings.Replace(match, domain, randomDomain, 1)
|
|
||||||
}
|
}
|
||||||
return match
|
return match
|
||||||
})
|
})
|
||||||
|
@ -46,11 +46,59 @@ func TestAnonymizeIP(t *testing.T) {
|
|||||||
|
|
||||||
func TestAnonymizeDNSLogLine(t *testing.T) {
|
func TestAnonymizeDNSLogLine(t *testing.T) {
|
||||||
anonymizer := anonymize.NewAnonymizer(netip.Addr{}, netip.Addr{})
|
anonymizer := anonymize.NewAnonymizer(netip.Addr{}, netip.Addr{})
|
||||||
testLog := `2024-04-23T20:01:11+02:00 TRAC client/internal/dns/local.go:25: received question: dns.Question{Name:"example.com", Qtype:0x1c, Qclass:0x1}`
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
original string
|
||||||
|
expect string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Basic domain with trailing content",
|
||||||
|
input: "received DNS request for DNS forwarder: domain=example.com: something happened with code=123",
|
||||||
|
original: "example.com",
|
||||||
|
expect: `received DNS request for DNS forwarder: domain=anon-[a-zA-Z0-9]+\.domain: something happened with code=123`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Domain with trailing dot",
|
||||||
|
input: "domain=example.com. processing request with status=pending",
|
||||||
|
original: "example.com",
|
||||||
|
expect: `domain=anon-[a-zA-Z0-9]+\.domain\. processing request with status=pending`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiple domains in log",
|
||||||
|
input: "forward domain=first.com status=ok, redirect to domain=second.com port=443",
|
||||||
|
original: "first.com", // testing just one is sufficient as AnonymizeDomain is tested separately
|
||||||
|
expect: `forward domain=anon-[a-zA-Z0-9]+\.domain status=ok, redirect to domain=anon-[a-zA-Z0-9]+\.domain port=443`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Already anonymized domain",
|
||||||
|
input: "got request domain=anon-xyz123.domain from=client1 to=server2",
|
||||||
|
original: "", // nothing should be anonymized
|
||||||
|
expect: `got request domain=anon-xyz123\.domain from=client1 to=server2`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Subdomain with trailing dot",
|
||||||
|
input: "domain=sub.example.com. next_hop=10.0.0.1 proto=udp",
|
||||||
|
original: "example.com",
|
||||||
|
expect: `domain=sub\.anon-[a-zA-Z0-9]+\.domain\. next_hop=10\.0\.0\.1 proto=udp`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Handler chain pattern log",
|
||||||
|
input: "pattern: domain=example.com. original: domain=*.example.com. wildcard=true priority=100",
|
||||||
|
original: "example.com",
|
||||||
|
expect: `pattern: domain=anon-[a-zA-Z0-9]+\.domain\. original: domain=\*\.anon-[a-zA-Z0-9]+\.domain\. wildcard=true priority=100`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
result := anonymizer.AnonymizeDNSLogLine(testLog)
|
for _, tc := range tests {
|
||||||
require.NotEqual(t, testLog, result)
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
assert.NotContains(t, result, "example.com")
|
result := anonymizer.AnonymizeDNSLogLine(tc.input)
|
||||||
|
if tc.original != "" {
|
||||||
|
assert.NotContains(t, result, tc.original)
|
||||||
|
}
|
||||||
|
assert.Regexp(t, tc.expect, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAnonymizeDomain(t *testing.T) {
|
func TestAnonymizeDomain(t *testing.T) {
|
||||||
|
173
client/cmd/networks.go
Normal file
173
client/cmd/networks.go
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var appendFlag bool
|
||||||
|
|
||||||
|
var networksCMD = &cobra.Command{
|
||||||
|
Use: "networks",
|
||||||
|
Aliases: []string{"routes"},
|
||||||
|
Short: "Manage networks",
|
||||||
|
Long: `Commands to list, select, or deselect networks. Replaces the "routes" command.`,
|
||||||
|
}
|
||||||
|
|
||||||
|
var routesListCmd = &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Short: "List networks",
|
||||||
|
Example: " netbird networks list",
|
||||||
|
Long: "List all available network routes.",
|
||||||
|
RunE: networksList,
|
||||||
|
}
|
||||||
|
|
||||||
|
var routesSelectCmd = &cobra.Command{
|
||||||
|
Use: "select network...|all",
|
||||||
|
Short: "Select network",
|
||||||
|
Long: "Select a list of networks by identifiers or 'all' to clear all selections and to accept all (including new) networks.\nDefault mode is replace, use -a to append to already selected networks.",
|
||||||
|
Example: " netbird networks select all\n netbird networks select route1 route2\n netbird routes select -a route3",
|
||||||
|
Args: cobra.MinimumNArgs(1),
|
||||||
|
RunE: networksSelect,
|
||||||
|
}
|
||||||
|
|
||||||
|
var routesDeselectCmd = &cobra.Command{
|
||||||
|
Use: "deselect network...|all",
|
||||||
|
Short: "Deselect networks",
|
||||||
|
Long: "Deselect previously selected networks by identifiers or 'all' to disable accepting any networks.",
|
||||||
|
Example: " netbird networks deselect all\n netbird networks deselect route1 route2",
|
||||||
|
Args: cobra.MinimumNArgs(1),
|
||||||
|
RunE: networksDeselect,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
routesSelectCmd.PersistentFlags().BoolVarP(&appendFlag, "append", "a", false, "Append to current network selection instead of replacing")
|
||||||
|
}
|
||||||
|
|
||||||
|
func networksList(cmd *cobra.Command, _ []string) error {
|
||||||
|
conn, err := getClient(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
client := proto.NewDaemonServiceClient(conn)
|
||||||
|
resp, err := client.ListNetworks(cmd.Context(), &proto.ListNetworksRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to list network: %v", status.Convert(err).Message())
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.Routes) == 0 {
|
||||||
|
cmd.Println("No networks available.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
printNetworks(cmd, resp)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func printNetworks(cmd *cobra.Command, resp *proto.ListNetworksResponse) {
|
||||||
|
cmd.Println("Available Networks:")
|
||||||
|
for _, route := range resp.Routes {
|
||||||
|
printNetwork(cmd, route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printNetwork(cmd *cobra.Command, route *proto.Network) {
|
||||||
|
selectedStatus := getSelectedStatus(route)
|
||||||
|
domains := route.GetDomains()
|
||||||
|
|
||||||
|
if len(domains) > 0 {
|
||||||
|
printDomainRoute(cmd, route, domains, selectedStatus)
|
||||||
|
} else {
|
||||||
|
printNetworkRoute(cmd, route, selectedStatus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSelectedStatus(route *proto.Network) string {
|
||||||
|
if route.GetSelected() {
|
||||||
|
return "Selected"
|
||||||
|
}
|
||||||
|
return "Not Selected"
|
||||||
|
}
|
||||||
|
|
||||||
|
func printDomainRoute(cmd *cobra.Command, route *proto.Network, domains []string, selectedStatus string) {
|
||||||
|
cmd.Printf("\n - ID: %s\n Domains: %s\n Status: %s\n", route.GetID(), strings.Join(domains, ", "), selectedStatus)
|
||||||
|
resolvedIPs := route.GetResolvedIPs()
|
||||||
|
|
||||||
|
if len(resolvedIPs) > 0 {
|
||||||
|
printResolvedIPs(cmd, domains, resolvedIPs)
|
||||||
|
} else {
|
||||||
|
cmd.Printf(" Resolved IPs: -\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printNetworkRoute(cmd *cobra.Command, route *proto.Network, selectedStatus string) {
|
||||||
|
cmd.Printf("\n - ID: %s\n Network: %s\n Status: %s\n", route.GetID(), route.GetRange(), selectedStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printResolvedIPs(cmd *cobra.Command, _ []string, resolvedIPs map[string]*proto.IPList) {
|
||||||
|
cmd.Printf(" Resolved IPs:\n")
|
||||||
|
for resolvedDomain, ipList := range resolvedIPs {
|
||||||
|
cmd.Printf(" [%s]: %s\n", resolvedDomain, strings.Join(ipList.GetIps(), ", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func networksSelect(cmd *cobra.Command, args []string) error {
|
||||||
|
conn, err := getClient(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
client := proto.NewDaemonServiceClient(conn)
|
||||||
|
req := &proto.SelectNetworksRequest{
|
||||||
|
NetworkIDs: args,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) == 1 && args[0] == "all" {
|
||||||
|
req.All = true
|
||||||
|
} else if appendFlag {
|
||||||
|
req.Append = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := client.SelectNetworks(cmd.Context(), req); err != nil {
|
||||||
|
return fmt.Errorf("failed to select networks: %v", status.Convert(err).Message())
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Println("Networks selected successfully.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func networksDeselect(cmd *cobra.Command, args []string) error {
|
||||||
|
conn, err := getClient(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
client := proto.NewDaemonServiceClient(conn)
|
||||||
|
req := &proto.SelectNetworksRequest{
|
||||||
|
NetworkIDs: args,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) == 1 && args[0] == "all" {
|
||||||
|
req.All = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := client.DeselectNetworks(cmd.Context(), req); err != nil {
|
||||||
|
return fmt.Errorf("failed to deselect networks: %v", status.Convert(err).Message())
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Println("Networks deselected successfully.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -142,14 +142,14 @@ func init() {
|
|||||||
rootCmd.AddCommand(loginCmd)
|
rootCmd.AddCommand(loginCmd)
|
||||||
rootCmd.AddCommand(versionCmd)
|
rootCmd.AddCommand(versionCmd)
|
||||||
rootCmd.AddCommand(sshCmd)
|
rootCmd.AddCommand(sshCmd)
|
||||||
rootCmd.AddCommand(routesCmd)
|
rootCmd.AddCommand(networksCMD)
|
||||||
rootCmd.AddCommand(debugCmd)
|
rootCmd.AddCommand(debugCmd)
|
||||||
|
|
||||||
serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd) // service control commands are subcommands of service
|
serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd) // service control commands are subcommands of service
|
||||||
serviceCmd.AddCommand(installCmd, uninstallCmd) // service installer commands are subcommands of service
|
serviceCmd.AddCommand(installCmd, uninstallCmd) // service installer commands are subcommands of service
|
||||||
|
|
||||||
routesCmd.AddCommand(routesListCmd)
|
networksCMD.AddCommand(routesListCmd)
|
||||||
routesCmd.AddCommand(routesSelectCmd, routesDeselectCmd)
|
networksCMD.AddCommand(routesSelectCmd, routesDeselectCmd)
|
||||||
|
|
||||||
debugCmd.AddCommand(debugBundleCmd)
|
debugCmd.AddCommand(debugBundleCmd)
|
||||||
debugCmd.AddCommand(logCmd)
|
debugCmd.AddCommand(logCmd)
|
||||||
|
@ -1,174 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
var appendFlag bool
|
|
||||||
|
|
||||||
var routesCmd = &cobra.Command{
|
|
||||||
Use: "routes",
|
|
||||||
Short: "Manage network routes",
|
|
||||||
Long: `Commands to list, select, or deselect network routes.`,
|
|
||||||
}
|
|
||||||
|
|
||||||
var routesListCmd = &cobra.Command{
|
|
||||||
Use: "list",
|
|
||||||
Aliases: []string{"ls"},
|
|
||||||
Short: "List routes",
|
|
||||||
Example: " netbird routes list",
|
|
||||||
Long: "List all available network routes.",
|
|
||||||
RunE: routesList,
|
|
||||||
}
|
|
||||||
|
|
||||||
var routesSelectCmd = &cobra.Command{
|
|
||||||
Use: "select route...|all",
|
|
||||||
Short: "Select routes",
|
|
||||||
Long: "Select a list of routes by identifiers or 'all' to clear all selections and to accept all (including new) routes.\nDefault mode is replace, use -a to append to already selected routes.",
|
|
||||||
Example: " netbird routes select all\n netbird routes select route1 route2\n netbird routes select -a route3",
|
|
||||||
Args: cobra.MinimumNArgs(1),
|
|
||||||
RunE: routesSelect,
|
|
||||||
}
|
|
||||||
|
|
||||||
var routesDeselectCmd = &cobra.Command{
|
|
||||||
Use: "deselect route...|all",
|
|
||||||
Short: "Deselect routes",
|
|
||||||
Long: "Deselect previously selected routes by identifiers or 'all' to disable accepting any routes.",
|
|
||||||
Example: " netbird routes deselect all\n netbird routes deselect route1 route2",
|
|
||||||
Args: cobra.MinimumNArgs(1),
|
|
||||||
RunE: routesDeselect,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
routesSelectCmd.PersistentFlags().BoolVarP(&appendFlag, "append", "a", false, "Append to current route selection instead of replacing")
|
|
||||||
}
|
|
||||||
|
|
||||||
func routesList(cmd *cobra.Command, _ []string) error {
|
|
||||||
conn, err := getClient(cmd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
client := proto.NewDaemonServiceClient(conn)
|
|
||||||
resp, err := client.ListRoutes(cmd.Context(), &proto.ListRoutesRequest{})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to list routes: %v", status.Convert(err).Message())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(resp.Routes) == 0 {
|
|
||||||
cmd.Println("No routes available.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
printRoutes(cmd, resp)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func printRoutes(cmd *cobra.Command, resp *proto.ListRoutesResponse) {
|
|
||||||
cmd.Println("Available Routes:")
|
|
||||||
for _, route := range resp.Routes {
|
|
||||||
printRoute(cmd, route)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func printRoute(cmd *cobra.Command, route *proto.Route) {
|
|
||||||
selectedStatus := getSelectedStatus(route)
|
|
||||||
domains := route.GetDomains()
|
|
||||||
|
|
||||||
if len(domains) > 0 {
|
|
||||||
printDomainRoute(cmd, route, domains, selectedStatus)
|
|
||||||
} else {
|
|
||||||
printNetworkRoute(cmd, route, selectedStatus)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSelectedStatus(route *proto.Route) string {
|
|
||||||
if route.GetSelected() {
|
|
||||||
return "Selected"
|
|
||||||
}
|
|
||||||
return "Not Selected"
|
|
||||||
}
|
|
||||||
|
|
||||||
func printDomainRoute(cmd *cobra.Command, route *proto.Route, domains []string, selectedStatus string) {
|
|
||||||
cmd.Printf("\n - ID: %s\n Domains: %s\n Status: %s\n", route.GetID(), strings.Join(domains, ", "), selectedStatus)
|
|
||||||
resolvedIPs := route.GetResolvedIPs()
|
|
||||||
|
|
||||||
if len(resolvedIPs) > 0 {
|
|
||||||
printResolvedIPs(cmd, domains, resolvedIPs)
|
|
||||||
} else {
|
|
||||||
cmd.Printf(" Resolved IPs: -\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func printNetworkRoute(cmd *cobra.Command, route *proto.Route, selectedStatus string) {
|
|
||||||
cmd.Printf("\n - ID: %s\n Network: %s\n Status: %s\n", route.GetID(), route.GetNetwork(), selectedStatus)
|
|
||||||
}
|
|
||||||
|
|
||||||
func printResolvedIPs(cmd *cobra.Command, domains []string, resolvedIPs map[string]*proto.IPList) {
|
|
||||||
cmd.Printf(" Resolved IPs:\n")
|
|
||||||
for _, domain := range domains {
|
|
||||||
if ipList, exists := resolvedIPs[domain]; exists {
|
|
||||||
cmd.Printf(" [%s]: %s\n", domain, strings.Join(ipList.GetIps(), ", "))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func routesSelect(cmd *cobra.Command, args []string) error {
|
|
||||||
conn, err := getClient(cmd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
client := proto.NewDaemonServiceClient(conn)
|
|
||||||
req := &proto.SelectRoutesRequest{
|
|
||||||
RouteIDs: args,
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) == 1 && args[0] == "all" {
|
|
||||||
req.All = true
|
|
||||||
} else if appendFlag {
|
|
||||||
req.Append = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := client.SelectRoutes(cmd.Context(), req); err != nil {
|
|
||||||
return fmt.Errorf("failed to select routes: %v", status.Convert(err).Message())
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Println("Routes selected successfully.")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func routesDeselect(cmd *cobra.Command, args []string) error {
|
|
||||||
conn, err := getClient(cmd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
client := proto.NewDaemonServiceClient(conn)
|
|
||||||
req := &proto.SelectRoutesRequest{
|
|
||||||
RouteIDs: args,
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) == 1 && args[0] == "all" {
|
|
||||||
req.All = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := client.DeselectRoutes(cmd.Context(), req); err != nil {
|
|
||||||
return fmt.Errorf("failed to deselect routes: %v", status.Convert(err).Message())
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Println("Routes deselected successfully.")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -40,6 +40,7 @@ type peerStateDetailOutput struct {
|
|||||||
Latency time.Duration `json:"latency" yaml:"latency"`
|
Latency time.Duration `json:"latency" yaml:"latency"`
|
||||||
RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"`
|
RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"`
|
||||||
Routes []string `json:"routes" yaml:"routes"`
|
Routes []string `json:"routes" yaml:"routes"`
|
||||||
|
Networks []string `json:"networks" yaml:"networks"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type peersStateOutput struct {
|
type peersStateOutput struct {
|
||||||
@ -98,6 +99,7 @@ type statusOutputOverview struct {
|
|||||||
RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"`
|
RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"`
|
||||||
RosenpassPermissive bool `json:"quantumResistancePermissive" yaml:"quantumResistancePermissive"`
|
RosenpassPermissive bool `json:"quantumResistancePermissive" yaml:"quantumResistancePermissive"`
|
||||||
Routes []string `json:"routes" yaml:"routes"`
|
Routes []string `json:"routes" yaml:"routes"`
|
||||||
|
Networks []string `json:"networks" yaml:"networks"`
|
||||||
NSServerGroups []nsServerGroupStateOutput `json:"dnsServers" yaml:"dnsServers"`
|
NSServerGroups []nsServerGroupStateOutput `json:"dnsServers" yaml:"dnsServers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +284,8 @@ func convertToStatusOutputOverview(resp *proto.StatusResponse) statusOutputOverv
|
|||||||
FQDN: pbFullStatus.GetLocalPeerState().GetFqdn(),
|
FQDN: pbFullStatus.GetLocalPeerState().GetFqdn(),
|
||||||
RosenpassEnabled: pbFullStatus.GetLocalPeerState().GetRosenpassEnabled(),
|
RosenpassEnabled: pbFullStatus.GetLocalPeerState().GetRosenpassEnabled(),
|
||||||
RosenpassPermissive: pbFullStatus.GetLocalPeerState().GetRosenpassPermissive(),
|
RosenpassPermissive: pbFullStatus.GetLocalPeerState().GetRosenpassPermissive(),
|
||||||
Routes: pbFullStatus.GetLocalPeerState().GetRoutes(),
|
Routes: pbFullStatus.GetLocalPeerState().GetNetworks(),
|
||||||
|
Networks: pbFullStatus.GetLocalPeerState().GetNetworks(),
|
||||||
NSServerGroups: mapNSGroups(pbFullStatus.GetDnsServers()),
|
NSServerGroups: mapNSGroups(pbFullStatus.GetDnsServers()),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,7 +393,8 @@ func mapPeers(peers []*proto.PeerState) peersStateOutput {
|
|||||||
TransferSent: transferSent,
|
TransferSent: transferSent,
|
||||||
Latency: pbPeerState.GetLatency().AsDuration(),
|
Latency: pbPeerState.GetLatency().AsDuration(),
|
||||||
RosenpassEnabled: pbPeerState.GetRosenpassEnabled(),
|
RosenpassEnabled: pbPeerState.GetRosenpassEnabled(),
|
||||||
Routes: pbPeerState.GetRoutes(),
|
Routes: pbPeerState.GetNetworks(),
|
||||||
|
Networks: pbPeerState.GetNetworks(),
|
||||||
}
|
}
|
||||||
|
|
||||||
peersStateDetail = append(peersStateDetail, peerState)
|
peersStateDetail = append(peersStateDetail, peerState)
|
||||||
@ -491,10 +495,10 @@ func parseGeneralSummary(overview statusOutputOverview, showURL bool, showRelays
|
|||||||
relaysString = fmt.Sprintf("%d/%d Available", overview.Relays.Available, overview.Relays.Total)
|
relaysString = fmt.Sprintf("%d/%d Available", overview.Relays.Available, overview.Relays.Total)
|
||||||
}
|
}
|
||||||
|
|
||||||
routes := "-"
|
networks := "-"
|
||||||
if len(overview.Routes) > 0 {
|
if len(overview.Networks) > 0 {
|
||||||
sort.Strings(overview.Routes)
|
sort.Strings(overview.Networks)
|
||||||
routes = strings.Join(overview.Routes, ", ")
|
networks = strings.Join(overview.Networks, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
var dnsServersString string
|
var dnsServersString string
|
||||||
@ -556,6 +560,7 @@ func parseGeneralSummary(overview statusOutputOverview, showURL bool, showRelays
|
|||||||
"Interface type: %s\n"+
|
"Interface type: %s\n"+
|
||||||
"Quantum resistance: %s\n"+
|
"Quantum resistance: %s\n"+
|
||||||
"Routes: %s\n"+
|
"Routes: %s\n"+
|
||||||
|
"Networks: %s\n"+
|
||||||
"Peers count: %s\n",
|
"Peers count: %s\n",
|
||||||
fmt.Sprintf("%s/%s%s", goos, goarch, goarm),
|
fmt.Sprintf("%s/%s%s", goos, goarch, goarm),
|
||||||
overview.DaemonVersion,
|
overview.DaemonVersion,
|
||||||
@ -568,7 +573,8 @@ func parseGeneralSummary(overview statusOutputOverview, showURL bool, showRelays
|
|||||||
interfaceIP,
|
interfaceIP,
|
||||||
interfaceTypeString,
|
interfaceTypeString,
|
||||||
rosenpassEnabledStatus,
|
rosenpassEnabledStatus,
|
||||||
routes,
|
networks,
|
||||||
|
networks,
|
||||||
peersCountString,
|
peersCountString,
|
||||||
)
|
)
|
||||||
return summary
|
return summary
|
||||||
@ -631,10 +637,10 @@ func parsePeers(peers peersStateOutput, rosenpassEnabled, rosenpassPermissive bo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
routes := "-"
|
networks := "-"
|
||||||
if len(peerState.Routes) > 0 {
|
if len(peerState.Networks) > 0 {
|
||||||
sort.Strings(peerState.Routes)
|
sort.Strings(peerState.Networks)
|
||||||
routes = strings.Join(peerState.Routes, ", ")
|
networks = strings.Join(peerState.Networks, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
peerString := fmt.Sprintf(
|
peerString := fmt.Sprintf(
|
||||||
@ -652,6 +658,7 @@ func parsePeers(peers peersStateOutput, rosenpassEnabled, rosenpassPermissive bo
|
|||||||
" Transfer status (received/sent) %s/%s\n"+
|
" Transfer status (received/sent) %s/%s\n"+
|
||||||
" Quantum resistance: %s\n"+
|
" Quantum resistance: %s\n"+
|
||||||
" Routes: %s\n"+
|
" Routes: %s\n"+
|
||||||
|
" Networks: %s\n"+
|
||||||
" Latency: %s\n",
|
" Latency: %s\n",
|
||||||
peerState.FQDN,
|
peerState.FQDN,
|
||||||
peerState.IP,
|
peerState.IP,
|
||||||
@ -668,7 +675,8 @@ func parsePeers(peers peersStateOutput, rosenpassEnabled, rosenpassPermissive bo
|
|||||||
toIEC(peerState.TransferReceived),
|
toIEC(peerState.TransferReceived),
|
||||||
toIEC(peerState.TransferSent),
|
toIEC(peerState.TransferSent),
|
||||||
rosenpassEnabledStatus,
|
rosenpassEnabledStatus,
|
||||||
routes,
|
networks,
|
||||||
|
networks,
|
||||||
peerState.Latency.String(),
|
peerState.Latency.String(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -810,6 +818,14 @@ func anonymizePeerDetail(a *anonymize.Anonymizer, peer *peerStateDetailOutput) {
|
|||||||
|
|
||||||
peer.RelayAddress = a.AnonymizeURI(peer.RelayAddress)
|
peer.RelayAddress = a.AnonymizeURI(peer.RelayAddress)
|
||||||
|
|
||||||
|
for i, route := range peer.Networks {
|
||||||
|
peer.Networks[i] = a.AnonymizeIPString(route)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, route := range peer.Networks {
|
||||||
|
peer.Networks[i] = a.AnonymizeRoute(route)
|
||||||
|
}
|
||||||
|
|
||||||
for i, route := range peer.Routes {
|
for i, route := range peer.Routes {
|
||||||
peer.Routes[i] = a.AnonymizeIPString(route)
|
peer.Routes[i] = a.AnonymizeIPString(route)
|
||||||
}
|
}
|
||||||
@ -850,6 +866,10 @@ func anonymizeOverview(a *anonymize.Anonymizer, overview *statusOutputOverview)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i, route := range overview.Networks {
|
||||||
|
overview.Networks[i] = a.AnonymizeRoute(route)
|
||||||
|
}
|
||||||
|
|
||||||
for i, route := range overview.Routes {
|
for i, route := range overview.Routes {
|
||||||
overview.Routes[i] = a.AnonymizeRoute(route)
|
overview.Routes[i] = a.AnonymizeRoute(route)
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ var resp = &proto.StatusResponse{
|
|||||||
LastWireguardHandshake: timestamppb.New(time.Date(2001, time.Month(1), 1, 1, 1, 2, 0, time.UTC)),
|
LastWireguardHandshake: timestamppb.New(time.Date(2001, time.Month(1), 1, 1, 1, 2, 0, time.UTC)),
|
||||||
BytesRx: 200,
|
BytesRx: 200,
|
||||||
BytesTx: 100,
|
BytesTx: 100,
|
||||||
Routes: []string{
|
Networks: []string{
|
||||||
"10.1.0.0/24",
|
"10.1.0.0/24",
|
||||||
},
|
},
|
||||||
Latency: durationpb.New(time.Duration(10000000)),
|
Latency: durationpb.New(time.Duration(10000000)),
|
||||||
@ -93,7 +93,7 @@ var resp = &proto.StatusResponse{
|
|||||||
PubKey: "Some-Pub-Key",
|
PubKey: "Some-Pub-Key",
|
||||||
KernelInterface: true,
|
KernelInterface: true,
|
||||||
Fqdn: "some-localhost.awesome-domain.com",
|
Fqdn: "some-localhost.awesome-domain.com",
|
||||||
Routes: []string{
|
Networks: []string{
|
||||||
"10.10.0.0/24",
|
"10.10.0.0/24",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -149,6 +149,9 @@ var overview = statusOutputOverview{
|
|||||||
Routes: []string{
|
Routes: []string{
|
||||||
"10.1.0.0/24",
|
"10.1.0.0/24",
|
||||||
},
|
},
|
||||||
|
Networks: []string{
|
||||||
|
"10.1.0.0/24",
|
||||||
|
},
|
||||||
Latency: time.Duration(10000000),
|
Latency: time.Duration(10000000),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -230,6 +233,9 @@ var overview = statusOutputOverview{
|
|||||||
Routes: []string{
|
Routes: []string{
|
||||||
"10.10.0.0/24",
|
"10.10.0.0/24",
|
||||||
},
|
},
|
||||||
|
Networks: []string{
|
||||||
|
"10.10.0.0/24",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConversionFromFullStatusToOutputOverview(t *testing.T) {
|
func TestConversionFromFullStatusToOutputOverview(t *testing.T) {
|
||||||
@ -295,6 +301,9 @@ func TestParsingToJSON(t *testing.T) {
|
|||||||
"quantumResistance": false,
|
"quantumResistance": false,
|
||||||
"routes": [
|
"routes": [
|
||||||
"10.1.0.0/24"
|
"10.1.0.0/24"
|
||||||
|
],
|
||||||
|
"networks": [
|
||||||
|
"10.1.0.0/24"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -318,7 +327,8 @@ func TestParsingToJSON(t *testing.T) {
|
|||||||
"transferSent": 1000,
|
"transferSent": 1000,
|
||||||
"latency": 10000000,
|
"latency": 10000000,
|
||||||
"quantumResistance": false,
|
"quantumResistance": false,
|
||||||
"routes": null
|
"routes": null,
|
||||||
|
"networks": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -359,6 +369,9 @@ func TestParsingToJSON(t *testing.T) {
|
|||||||
"routes": [
|
"routes": [
|
||||||
"10.10.0.0/24"
|
"10.10.0.0/24"
|
||||||
],
|
],
|
||||||
|
"networks": [
|
||||||
|
"10.10.0.0/24"
|
||||||
|
],
|
||||||
"dnsServers": [
|
"dnsServers": [
|
||||||
{
|
{
|
||||||
"servers": [
|
"servers": [
|
||||||
@ -418,6 +431,8 @@ func TestParsingToYAML(t *testing.T) {
|
|||||||
quantumResistance: false
|
quantumResistance: false
|
||||||
routes:
|
routes:
|
||||||
- 10.1.0.0/24
|
- 10.1.0.0/24
|
||||||
|
networks:
|
||||||
|
- 10.1.0.0/24
|
||||||
- fqdn: peer-2.awesome-domain.com
|
- fqdn: peer-2.awesome-domain.com
|
||||||
netbirdIp: 192.168.178.102
|
netbirdIp: 192.168.178.102
|
||||||
publicKey: Pubkey2
|
publicKey: Pubkey2
|
||||||
@ -437,6 +452,7 @@ func TestParsingToYAML(t *testing.T) {
|
|||||||
latency: 10ms
|
latency: 10ms
|
||||||
quantumResistance: false
|
quantumResistance: false
|
||||||
routes: []
|
routes: []
|
||||||
|
networks: []
|
||||||
cliVersion: development
|
cliVersion: development
|
||||||
daemonVersion: 0.14.1
|
daemonVersion: 0.14.1
|
||||||
management:
|
management:
|
||||||
@ -465,6 +481,8 @@ quantumResistance: false
|
|||||||
quantumResistancePermissive: false
|
quantumResistancePermissive: false
|
||||||
routes:
|
routes:
|
||||||
- 10.10.0.0/24
|
- 10.10.0.0/24
|
||||||
|
networks:
|
||||||
|
- 10.10.0.0/24
|
||||||
dnsServers:
|
dnsServers:
|
||||||
- servers:
|
- servers:
|
||||||
- 8.8.8.8:53
|
- 8.8.8.8:53
|
||||||
@ -509,6 +527,7 @@ func TestParsingToDetail(t *testing.T) {
|
|||||||
Transfer status (received/sent) 200 B/100 B
|
Transfer status (received/sent) 200 B/100 B
|
||||||
Quantum resistance: false
|
Quantum resistance: false
|
||||||
Routes: 10.1.0.0/24
|
Routes: 10.1.0.0/24
|
||||||
|
Networks: 10.1.0.0/24
|
||||||
Latency: 10ms
|
Latency: 10ms
|
||||||
|
|
||||||
peer-2.awesome-domain.com:
|
peer-2.awesome-domain.com:
|
||||||
@ -525,6 +544,7 @@ func TestParsingToDetail(t *testing.T) {
|
|||||||
Transfer status (received/sent) 2.0 KiB/1000 B
|
Transfer status (received/sent) 2.0 KiB/1000 B
|
||||||
Quantum resistance: false
|
Quantum resistance: false
|
||||||
Routes: -
|
Routes: -
|
||||||
|
Networks: -
|
||||||
Latency: 10ms
|
Latency: 10ms
|
||||||
|
|
||||||
OS: %s/%s
|
OS: %s/%s
|
||||||
@ -543,6 +563,7 @@ NetBird IP: 192.168.178.100/16
|
|||||||
Interface type: Kernel
|
Interface type: Kernel
|
||||||
Quantum resistance: false
|
Quantum resistance: false
|
||||||
Routes: 10.10.0.0/24
|
Routes: 10.10.0.0/24
|
||||||
|
Networks: 10.10.0.0/24
|
||||||
Peers count: 2/2 Connected
|
Peers count: 2/2 Connected
|
||||||
`, lastConnectionUpdate1, lastHandshake1, lastConnectionUpdate2, lastHandshake2, runtime.GOOS, runtime.GOARCH, overview.CliVersion)
|
`, lastConnectionUpdate1, lastHandshake1, lastConnectionUpdate2, lastHandshake2, runtime.GOOS, runtime.GOARCH, overview.CliVersion)
|
||||||
|
|
||||||
@ -564,6 +585,7 @@ NetBird IP: 192.168.178.100/16
|
|||||||
Interface type: Kernel
|
Interface type: Kernel
|
||||||
Quantum resistance: false
|
Quantum resistance: false
|
||||||
Routes: 10.10.0.0/24
|
Routes: 10.10.0.0/24
|
||||||
|
Networks: 10.10.0.0/24
|
||||||
Peers count: 2/2 Connected
|
Peers count: 2/2 Connected
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -10,6 +10,8 @@ import (
|
|||||||
"go.opentelemetry.io/otel"
|
"go.opentelemetry.io/otel"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
|
"github.com/netbirdio/netbird/management/server/settings"
|
||||||
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
@ -71,7 +73,7 @@ func startManagement(t *testing.T, config *mgmt.Config, testFile string) (*grpc.
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
s := grpc.NewServer()
|
s := grpc.NewServer()
|
||||||
store, cleanUp, err := mgmt.NewTestStoreFromSQL(context.Background(), testFile, t.TempDir())
|
store, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), testFile, t.TempDir())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -93,7 +95,7 @@ func startManagement(t *testing.T, config *mgmt.Config, testFile string) (*grpc.
|
|||||||
}
|
}
|
||||||
|
|
||||||
secretsManager := mgmt.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
|
secretsManager := mgmt.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
|
||||||
mgmtServer, err := mgmt.NewServer(context.Background(), config, accountManager, peersUpdateManager, secretsManager, nil, nil)
|
mgmtServer, err := mgmt.NewServer(context.Background(), config, accountManager, settings.NewManager(store), peersUpdateManager, secretsManager, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -332,18 +332,12 @@ func (m *aclManager) createDefaultChains() error {
|
|||||||
|
|
||||||
// The OUTPUT chain gets an extra rule to allow traffic to any set up routes, the return traffic is handled by the INPUT related/established rule.
|
// The OUTPUT chain gets an extra rule to allow traffic to any set up routes, the return traffic is handled by the INPUT related/established rule.
|
||||||
func (m *aclManager) seedInitialEntries() {
|
func (m *aclManager) seedInitialEntries() {
|
||||||
|
|
||||||
established := getConntrackEstablished()
|
established := getConntrackEstablished()
|
||||||
|
|
||||||
m.appendToEntries("INPUT", []string{"-i", m.wgIface.Name(), "-j", "DROP"})
|
m.appendToEntries("INPUT", []string{"-i", m.wgIface.Name(), "-j", "DROP"})
|
||||||
m.appendToEntries("INPUT", []string{"-i", m.wgIface.Name(), "-j", chainNameInputRules})
|
m.appendToEntries("INPUT", []string{"-i", m.wgIface.Name(), "-j", chainNameInputRules})
|
||||||
m.appendToEntries("INPUT", append([]string{"-i", m.wgIface.Name()}, established...))
|
m.appendToEntries("INPUT", append([]string{"-i", m.wgIface.Name()}, established...))
|
||||||
|
|
||||||
m.appendToEntries("OUTPUT", []string{"-o", m.wgIface.Name(), "-j", "DROP"})
|
|
||||||
m.appendToEntries("OUTPUT", []string{"-o", m.wgIface.Name(), "-j", chainNameOutputRules})
|
|
||||||
m.appendToEntries("OUTPUT", []string{"-o", m.wgIface.Name(), "!", "-d", m.wgIface.Address().String(), "-j", "ACCEPT"})
|
|
||||||
m.appendToEntries("OUTPUT", append([]string{"-o", m.wgIface.Name()}, established...))
|
|
||||||
|
|
||||||
m.appendToEntries("FORWARD", []string{"-i", m.wgIface.Name(), "-j", "DROP"})
|
m.appendToEntries("FORWARD", []string{"-i", m.wgIface.Name(), "-j", "DROP"})
|
||||||
m.appendToEntries("FORWARD", []string{"-i", m.wgIface.Name(), "-j", m.routingFwChainName})
|
m.appendToEntries("FORWARD", []string{"-i", m.wgIface.Name(), "-j", m.routingFwChainName})
|
||||||
m.appendToEntries("FORWARD", append([]string{"-o", m.wgIface.Name()}, established...))
|
m.appendToEntries("FORWARD", append([]string{"-o", m.wgIface.Name()}, established...))
|
||||||
|
@ -207,19 +207,9 @@ func (m *Manager) AllowNetbird() error {
|
|||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to allow netbird interface traffic: %w", err)
|
return fmt.Errorf("allow netbird interface traffic: %w", err)
|
||||||
}
|
}
|
||||||
_, err = m.AddPeerFiltering(
|
return nil
|
||||||
net.ParseIP("0.0.0.0"),
|
|
||||||
"all",
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
firewall.RuleDirectionOUT,
|
|
||||||
firewall.ActionAccept,
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush doesn't need to be implemented for this manager
|
// Flush doesn't need to be implemented for this manager
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -28,7 +27,6 @@ const (
|
|||||||
|
|
||||||
// filter chains contains the rules that jump to the rules chains
|
// filter chains contains the rules that jump to the rules chains
|
||||||
chainNameInputFilter = "netbird-acl-input-filter"
|
chainNameInputFilter = "netbird-acl-input-filter"
|
||||||
chainNameOutputFilter = "netbird-acl-output-filter"
|
|
||||||
chainNameForwardFilter = "netbird-acl-forward-filter"
|
chainNameForwardFilter = "netbird-acl-forward-filter"
|
||||||
chainNamePrerouting = "netbird-rt-prerouting"
|
chainNamePrerouting = "netbird-rt-prerouting"
|
||||||
|
|
||||||
@ -441,18 +439,6 @@ func (m *AclManager) createDefaultChains() (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// netbird-acl-output-filter
|
|
||||||
// type filter hook output priority filter; policy accept;
|
|
||||||
chain = m.createFilterChainWithHook(chainNameOutputFilter, nftables.ChainHookOutput)
|
|
||||||
m.addFwdAllow(chain, expr.MetaKeyOIFNAME)
|
|
||||||
m.addJumpRule(chain, m.chainOutputRules.Name, expr.MetaKeyOIFNAME) // to netbird-acl-output-rules
|
|
||||||
m.addDropExpressions(chain, expr.MetaKeyOIFNAME)
|
|
||||||
err = m.rConn.Flush()
|
|
||||||
if err != nil {
|
|
||||||
log.Debugf("failed to create chain (%s): %s", chainNameOutputFilter, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// netbird-acl-forward-filter
|
// netbird-acl-forward-filter
|
||||||
chainFwFilter := m.createFilterChainWithHook(chainNameForwardFilter, nftables.ChainHookForward)
|
chainFwFilter := m.createFilterChainWithHook(chainNameForwardFilter, nftables.ChainHookForward)
|
||||||
m.addJumpRulesToRtForward(chainFwFilter) // to netbird-rt-fwd
|
m.addJumpRulesToRtForward(chainFwFilter) // to netbird-rt-fwd
|
||||||
@ -619,45 +605,6 @@ func (m *AclManager) addDropExpressions(chain *nftables.Chain, ifaceKey expr.Met
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AclManager) addFwdAllow(chain *nftables.Chain, iifname expr.MetaKey) {
|
|
||||||
ip, _ := netip.AddrFromSlice(m.wgIface.Address().Network.IP.To4())
|
|
||||||
dstOp := expr.CmpOpNeq
|
|
||||||
expressions := []expr.Any{
|
|
||||||
&expr.Meta{Key: iifname, Register: 1},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: ifname(m.wgIface.Name()),
|
|
||||||
},
|
|
||||||
&expr.Payload{
|
|
||||||
DestRegister: 2,
|
|
||||||
Base: expr.PayloadBaseNetworkHeader,
|
|
||||||
Offset: 16,
|
|
||||||
Len: 4,
|
|
||||||
},
|
|
||||||
&expr.Bitwise{
|
|
||||||
SourceRegister: 2,
|
|
||||||
DestRegister: 2,
|
|
||||||
Len: 4,
|
|
||||||
Xor: []byte{0x0, 0x0, 0x0, 0x0},
|
|
||||||
Mask: m.wgIface.Address().Network.Mask,
|
|
||||||
},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: dstOp,
|
|
||||||
Register: 2,
|
|
||||||
Data: ip.Unmap().AsSlice(),
|
|
||||||
},
|
|
||||||
&expr.Verdict{
|
|
||||||
Kind: expr.VerdictAccept,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_ = m.rConn.AddRule(&nftables.Rule{
|
|
||||||
Table: chain.Table,
|
|
||||||
Chain: chain,
|
|
||||||
Exprs: expressions,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *AclManager) addJumpRule(chain *nftables.Chain, to string, ifaceKey expr.MetaKey) {
|
func (m *AclManager) addJumpRule(chain *nftables.Chain, to string, ifaceKey expr.MetaKey) {
|
||||||
expressions := []expr.Any{
|
expressions := []expr.Any{
|
||||||
&expr.Meta{Key: ifaceKey, Register: 1},
|
&expr.Meta{Key: ifaceKey, Register: 1},
|
||||||
|
@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
package uspfilter
|
package uspfilter
|
||||||
|
|
||||||
import "github.com/netbirdio/netbird/client/internal/statemanager"
|
import (
|
||||||
|
"github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/statemanager"
|
||||||
|
)
|
||||||
|
|
||||||
// Reset firewall to the default state
|
// Reset firewall to the default state
|
||||||
func (m *Manager) Reset(stateManager *statemanager.Manager) error {
|
func (m *Manager) Reset(stateManager *statemanager.Manager) error {
|
||||||
@ -12,6 +15,21 @@ func (m *Manager) Reset(stateManager *statemanager.Manager) error {
|
|||||||
m.outgoingRules = make(map[string]RuleSet)
|
m.outgoingRules = make(map[string]RuleSet)
|
||||||
m.incomingRules = make(map[string]RuleSet)
|
m.incomingRules = make(map[string]RuleSet)
|
||||||
|
|
||||||
|
if m.udpTracker != nil {
|
||||||
|
m.udpTracker.Close()
|
||||||
|
m.udpTracker = conntrack.NewUDPTracker(conntrack.DefaultUDPTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.icmpTracker != nil {
|
||||||
|
m.icmpTracker.Close()
|
||||||
|
m.icmpTracker = conntrack.NewICMPTracker(conntrack.DefaultICMPTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.tcpTracker != nil {
|
||||||
|
m.tcpTracker.Close()
|
||||||
|
m.tcpTracker = conntrack.NewTCPTracker(conntrack.DefaultTCPTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
if m.nativeFirewall != nil {
|
if m.nativeFirewall != nil {
|
||||||
return m.nativeFirewall.Reset(stateManager)
|
return m.nativeFirewall.Reset(stateManager)
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack"
|
||||||
"github.com/netbirdio/netbird/client/internal/statemanager"
|
"github.com/netbirdio/netbird/client/internal/statemanager"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -26,6 +27,21 @@ func (m *Manager) Reset(*statemanager.Manager) error {
|
|||||||
m.outgoingRules = make(map[string]RuleSet)
|
m.outgoingRules = make(map[string]RuleSet)
|
||||||
m.incomingRules = make(map[string]RuleSet)
|
m.incomingRules = make(map[string]RuleSet)
|
||||||
|
|
||||||
|
if m.udpTracker != nil {
|
||||||
|
m.udpTracker.Close()
|
||||||
|
m.udpTracker = conntrack.NewUDPTracker(conntrack.DefaultUDPTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.icmpTracker != nil {
|
||||||
|
m.icmpTracker.Close()
|
||||||
|
m.icmpTracker = conntrack.NewICMPTracker(conntrack.DefaultICMPTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.tcpTracker != nil {
|
||||||
|
m.tcpTracker.Close()
|
||||||
|
m.tcpTracker = conntrack.NewTCPTracker(conntrack.DefaultTCPTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
if !isWindowsFirewallReachable() {
|
if !isWindowsFirewallReachable() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
138
client/firewall/uspfilter/conntrack/common.go
Normal file
138
client/firewall/uspfilter/conntrack/common.go
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
// common.go
|
||||||
|
package conntrack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BaseConnTrack provides common fields and locking for all connection types
|
||||||
|
type BaseConnTrack struct {
|
||||||
|
sync.RWMutex
|
||||||
|
SourceIP net.IP
|
||||||
|
DestIP net.IP
|
||||||
|
SourcePort uint16
|
||||||
|
DestPort uint16
|
||||||
|
lastSeen atomic.Int64 // Unix nano for atomic access
|
||||||
|
established atomic.Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// these small methods will be inlined by the compiler
|
||||||
|
|
||||||
|
// UpdateLastSeen safely updates the last seen timestamp
|
||||||
|
func (b *BaseConnTrack) UpdateLastSeen() {
|
||||||
|
b.lastSeen.Store(time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEstablished safely checks if connection is established
|
||||||
|
func (b *BaseConnTrack) IsEstablished() bool {
|
||||||
|
return b.established.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEstablished safely sets the established state
|
||||||
|
func (b *BaseConnTrack) SetEstablished(state bool) {
|
||||||
|
b.established.Store(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLastSeen safely gets the last seen timestamp
|
||||||
|
func (b *BaseConnTrack) GetLastSeen() time.Time {
|
||||||
|
return time.Unix(0, b.lastSeen.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
// timeoutExceeded checks if the connection has exceeded the given timeout
|
||||||
|
func (b *BaseConnTrack) timeoutExceeded(timeout time.Duration) bool {
|
||||||
|
lastSeen := time.Unix(0, b.lastSeen.Load())
|
||||||
|
return time.Since(lastSeen) > timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPAddr is a fixed-size IP address to avoid allocations
|
||||||
|
type IPAddr [16]byte
|
||||||
|
|
||||||
|
// MakeIPAddr creates an IPAddr from net.IP
|
||||||
|
func MakeIPAddr(ip net.IP) (addr IPAddr) {
|
||||||
|
// Optimization: check for v4 first as it's more common
|
||||||
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
|
copy(addr[12:], ip4)
|
||||||
|
} else {
|
||||||
|
copy(addr[:], ip.To16())
|
||||||
|
}
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnKey uniquely identifies a connection
|
||||||
|
type ConnKey struct {
|
||||||
|
SrcIP IPAddr
|
||||||
|
DstIP IPAddr
|
||||||
|
SrcPort uint16
|
||||||
|
DstPort uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeConnKey creates a connection key
|
||||||
|
func makeConnKey(srcIP net.IP, dstIP net.IP, srcPort uint16, dstPort uint16) ConnKey {
|
||||||
|
return ConnKey{
|
||||||
|
SrcIP: MakeIPAddr(srcIP),
|
||||||
|
DstIP: MakeIPAddr(dstIP),
|
||||||
|
SrcPort: srcPort,
|
||||||
|
DstPort: dstPort,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateIPs checks if IPs match without allocation
|
||||||
|
func ValidateIPs(connIP IPAddr, pktIP net.IP) bool {
|
||||||
|
if ip4 := pktIP.To4(); ip4 != nil {
|
||||||
|
// Compare IPv4 addresses (last 4 bytes)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
if connIP[12+i] != ip4[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Compare full IPv6 addresses
|
||||||
|
ip6 := pktIP.To16()
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
if connIP[i] != ip6[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreallocatedIPs is a pool of IP byte slices to reduce allocations
|
||||||
|
type PreallocatedIPs struct {
|
||||||
|
sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPreallocatedIPs creates a new IP pool
|
||||||
|
func NewPreallocatedIPs() *PreallocatedIPs {
|
||||||
|
return &PreallocatedIPs{
|
||||||
|
Pool: sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
ip := make(net.IP, 16)
|
||||||
|
return &ip
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves an IP from the pool
|
||||||
|
func (p *PreallocatedIPs) Get() net.IP {
|
||||||
|
return *p.Pool.Get().(*net.IP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put returns an IP to the pool
|
||||||
|
func (p *PreallocatedIPs) Put(ip net.IP) {
|
||||||
|
p.Pool.Put(&ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyIP copies an IP address efficiently
|
||||||
|
func copyIP(dst, src net.IP) {
|
||||||
|
if len(src) == 16 {
|
||||||
|
copy(dst, src)
|
||||||
|
} else {
|
||||||
|
// Handle IPv4
|
||||||
|
copy(dst[12:], src.To4())
|
||||||
|
}
|
||||||
|
}
|
115
client/firewall/uspfilter/conntrack/common_test.go
Normal file
115
client/firewall/uspfilter/conntrack/common_test.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package conntrack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkIPOperations(b *testing.B) {
|
||||||
|
b.Run("MakeIPAddr", func(b *testing.B) {
|
||||||
|
ip := net.ParseIP("192.168.1.1")
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = MakeIPAddr(ip)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("ValidateIPs", func(b *testing.B) {
|
||||||
|
ip1 := net.ParseIP("192.168.1.1")
|
||||||
|
ip2 := net.ParseIP("192.168.1.1")
|
||||||
|
addr := MakeIPAddr(ip1)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = ValidateIPs(addr, ip2)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("IPPool", func(b *testing.B) {
|
||||||
|
pool := NewPreallocatedIPs()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ip := pool.Get()
|
||||||
|
pool.Put(ip)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
func BenchmarkAtomicOperations(b *testing.B) {
|
||||||
|
conn := &BaseConnTrack{}
|
||||||
|
b.Run("UpdateLastSeen", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
conn.UpdateLastSeen()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("IsEstablished", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = conn.IsEstablished()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("SetEstablished", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
conn.SetEstablished(i%2 == 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("GetLastSeen", func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = conn.GetLastSeen()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory pressure tests
|
||||||
|
func BenchmarkMemoryPressure(b *testing.B) {
|
||||||
|
b.Run("TCPHighLoad", func(b *testing.B) {
|
||||||
|
tracker := NewTCPTracker(DefaultTCPTimeout)
|
||||||
|
defer tracker.Close()
|
||||||
|
|
||||||
|
// Generate different IPs
|
||||||
|
srcIPs := make([]net.IP, 100)
|
||||||
|
dstIPs := make([]net.IP, 100)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
srcIPs[i] = net.IPv4(192, 168, byte(i/256), byte(i%256))
|
||||||
|
dstIPs[i] = net.IPv4(10, 0, byte(i/256), byte(i%256))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
srcIdx := i % len(srcIPs)
|
||||||
|
dstIdx := (i + 1) % len(dstIPs)
|
||||||
|
tracker.TrackOutbound(srcIPs[srcIdx], dstIPs[dstIdx], uint16(i%65535), 80, TCPSyn)
|
||||||
|
|
||||||
|
// Simulate some valid inbound packets
|
||||||
|
if i%3 == 0 {
|
||||||
|
tracker.IsValidInbound(dstIPs[dstIdx], srcIPs[srcIdx], 80, uint16(i%65535), TCPAck)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("UDPHighLoad", func(b *testing.B) {
|
||||||
|
tracker := NewUDPTracker(DefaultUDPTimeout)
|
||||||
|
defer tracker.Close()
|
||||||
|
|
||||||
|
// Generate different IPs
|
||||||
|
srcIPs := make([]net.IP, 100)
|
||||||
|
dstIPs := make([]net.IP, 100)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
srcIPs[i] = net.IPv4(192, 168, byte(i/256), byte(i%256))
|
||||||
|
dstIPs[i] = net.IPv4(10, 0, byte(i/256), byte(i%256))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
srcIdx := i % len(srcIPs)
|
||||||
|
dstIdx := (i + 1) % len(dstIPs)
|
||||||
|
tracker.TrackOutbound(srcIPs[srcIdx], dstIPs[dstIdx], uint16(i%65535), 80)
|
||||||
|
|
||||||
|
// Simulate some valid inbound packets
|
||||||
|
if i%3 == 0 {
|
||||||
|
tracker.IsValidInbound(dstIPs[dstIdx], srcIPs[srcIdx], 80, uint16(i%65535))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
170
client/firewall/uspfilter/conntrack/icmp.go
Normal file
170
client/firewall/uspfilter/conntrack/icmp.go
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
package conntrack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultICMPTimeout is the default timeout for ICMP connections
|
||||||
|
DefaultICMPTimeout = 30 * time.Second
|
||||||
|
// ICMPCleanupInterval is how often we check for stale ICMP connections
|
||||||
|
ICMPCleanupInterval = 15 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
// ICMPConnKey uniquely identifies an ICMP connection
|
||||||
|
type ICMPConnKey struct {
|
||||||
|
// Supports both IPv4 and IPv6
|
||||||
|
SrcIP [16]byte
|
||||||
|
DstIP [16]byte
|
||||||
|
Sequence uint16 // ICMP sequence number
|
||||||
|
ID uint16 // ICMP identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
// ICMPConnTrack represents an ICMP connection state
|
||||||
|
type ICMPConnTrack struct {
|
||||||
|
BaseConnTrack
|
||||||
|
Sequence uint16
|
||||||
|
ID uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// ICMPTracker manages ICMP connection states
|
||||||
|
type ICMPTracker struct {
|
||||||
|
connections map[ICMPConnKey]*ICMPConnTrack
|
||||||
|
timeout time.Duration
|
||||||
|
cleanupTicker *time.Ticker
|
||||||
|
mutex sync.RWMutex
|
||||||
|
done chan struct{}
|
||||||
|
ipPool *PreallocatedIPs
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewICMPTracker creates a new ICMP connection tracker
|
||||||
|
func NewICMPTracker(timeout time.Duration) *ICMPTracker {
|
||||||
|
if timeout == 0 {
|
||||||
|
timeout = DefaultICMPTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker := &ICMPTracker{
|
||||||
|
connections: make(map[ICMPConnKey]*ICMPConnTrack),
|
||||||
|
timeout: timeout,
|
||||||
|
cleanupTicker: time.NewTicker(ICMPCleanupInterval),
|
||||||
|
done: make(chan struct{}),
|
||||||
|
ipPool: NewPreallocatedIPs(),
|
||||||
|
}
|
||||||
|
|
||||||
|
go tracker.cleanupRoutine()
|
||||||
|
return tracker
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrackOutbound records an outbound ICMP Echo Request
|
||||||
|
func (t *ICMPTracker) TrackOutbound(srcIP net.IP, dstIP net.IP, id uint16, seq uint16) {
|
||||||
|
key := makeICMPKey(srcIP, dstIP, id, seq)
|
||||||
|
now := time.Now().UnixNano()
|
||||||
|
|
||||||
|
t.mutex.Lock()
|
||||||
|
conn, exists := t.connections[key]
|
||||||
|
if !exists {
|
||||||
|
srcIPCopy := t.ipPool.Get()
|
||||||
|
dstIPCopy := t.ipPool.Get()
|
||||||
|
copyIP(srcIPCopy, srcIP)
|
||||||
|
copyIP(dstIPCopy, dstIP)
|
||||||
|
|
||||||
|
conn = &ICMPConnTrack{
|
||||||
|
BaseConnTrack: BaseConnTrack{
|
||||||
|
SourceIP: srcIPCopy,
|
||||||
|
DestIP: dstIPCopy,
|
||||||
|
},
|
||||||
|
ID: id,
|
||||||
|
Sequence: seq,
|
||||||
|
}
|
||||||
|
conn.lastSeen.Store(now)
|
||||||
|
conn.established.Store(true)
|
||||||
|
t.connections[key] = conn
|
||||||
|
}
|
||||||
|
t.mutex.Unlock()
|
||||||
|
|
||||||
|
conn.lastSeen.Store(now)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidInbound checks if an inbound ICMP Echo Reply matches a tracked request
|
||||||
|
func (t *ICMPTracker) IsValidInbound(srcIP net.IP, dstIP net.IP, id uint16, seq uint16, icmpType uint8) bool {
|
||||||
|
switch icmpType {
|
||||||
|
case uint8(layers.ICMPv4TypeDestinationUnreachable),
|
||||||
|
uint8(layers.ICMPv4TypeTimeExceeded):
|
||||||
|
return true
|
||||||
|
case uint8(layers.ICMPv4TypeEchoReply):
|
||||||
|
// continue processing
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
key := makeICMPKey(dstIP, srcIP, id, seq)
|
||||||
|
|
||||||
|
t.mutex.RLock()
|
||||||
|
conn, exists := t.connections[key]
|
||||||
|
t.mutex.RUnlock()
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn.timeoutExceeded(t.timeout) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn.IsEstablished() &&
|
||||||
|
ValidateIPs(MakeIPAddr(srcIP), conn.DestIP) &&
|
||||||
|
ValidateIPs(MakeIPAddr(dstIP), conn.SourceIP) &&
|
||||||
|
conn.ID == id &&
|
||||||
|
conn.Sequence == seq
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ICMPTracker) cleanupRoutine() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-t.cleanupTicker.C:
|
||||||
|
t.cleanup()
|
||||||
|
case <-t.done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (t *ICMPTracker) cleanup() {
|
||||||
|
t.mutex.Lock()
|
||||||
|
defer t.mutex.Unlock()
|
||||||
|
|
||||||
|
for key, conn := range t.connections {
|
||||||
|
if conn.timeoutExceeded(t.timeout) {
|
||||||
|
t.ipPool.Put(conn.SourceIP)
|
||||||
|
t.ipPool.Put(conn.DestIP)
|
||||||
|
delete(t.connections, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close stops the cleanup routine and releases resources
|
||||||
|
func (t *ICMPTracker) Close() {
|
||||||
|
t.cleanupTicker.Stop()
|
||||||
|
close(t.done)
|
||||||
|
|
||||||
|
t.mutex.Lock()
|
||||||
|
for _, conn := range t.connections {
|
||||||
|
t.ipPool.Put(conn.SourceIP)
|
||||||
|
t.ipPool.Put(conn.DestIP)
|
||||||
|
}
|
||||||
|
t.connections = nil
|
||||||
|
t.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeICMPKey creates an ICMP connection key
|
||||||
|
func makeICMPKey(srcIP net.IP, dstIP net.IP, id uint16, seq uint16) ICMPConnKey {
|
||||||
|
return ICMPConnKey{
|
||||||
|
SrcIP: MakeIPAddr(srcIP),
|
||||||
|
DstIP: MakeIPAddr(dstIP),
|
||||||
|
ID: id,
|
||||||
|
Sequence: seq,
|
||||||
|
}
|
||||||
|
}
|
39
client/firewall/uspfilter/conntrack/icmp_test.go
Normal file
39
client/firewall/uspfilter/conntrack/icmp_test.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package conntrack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkICMPTracker(b *testing.B) {
|
||||||
|
b.Run("TrackOutbound", func(b *testing.B) {
|
||||||
|
tracker := NewICMPTracker(DefaultICMPTimeout)
|
||||||
|
defer tracker.Close()
|
||||||
|
|
||||||
|
srcIP := net.ParseIP("192.168.1.1")
|
||||||
|
dstIP := net.ParseIP("192.168.1.2")
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, uint16(i%65535), uint16(i%65535))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("IsValidInbound", func(b *testing.B) {
|
||||||
|
tracker := NewICMPTracker(DefaultICMPTimeout)
|
||||||
|
defer tracker.Close()
|
||||||
|
|
||||||
|
srcIP := net.ParseIP("192.168.1.1")
|
||||||
|
dstIP := net.ParseIP("192.168.1.2")
|
||||||
|
|
||||||
|
// Pre-populate some connections
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, uint16(i), uint16(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
tracker.IsValidInbound(dstIP, srcIP, uint16(i%1000), uint16(i%1000), 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
376
client/firewall/uspfilter/conntrack/tcp.go
Normal file
376
client/firewall/uspfilter/conntrack/tcp.go
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
package conntrack
|
||||||
|
|
||||||
|
// TODO: Send RST packets for invalid/timed-out connections
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MSL (Maximum Segment Lifetime) is typically 2 minutes
|
||||||
|
MSL = 2 * time.Minute
|
||||||
|
// TimeWaitTimeout (TIME-WAIT) should last 2*MSL
|
||||||
|
TimeWaitTimeout = 2 * MSL
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TCPSyn uint8 = 0x02
|
||||||
|
TCPAck uint8 = 0x10
|
||||||
|
TCPFin uint8 = 0x01
|
||||||
|
TCPRst uint8 = 0x04
|
||||||
|
TCPPush uint8 = 0x08
|
||||||
|
TCPUrg uint8 = 0x20
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultTCPTimeout is the default timeout for established TCP connections
|
||||||
|
DefaultTCPTimeout = 3 * time.Hour
|
||||||
|
// TCPHandshakeTimeout is timeout for TCP handshake completion
|
||||||
|
TCPHandshakeTimeout = 60 * time.Second
|
||||||
|
// TCPCleanupInterval is how often we check for stale connections
|
||||||
|
TCPCleanupInterval = 5 * time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
|
// TCPState represents the state of a TCP connection
|
||||||
|
type TCPState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
TCPStateNew TCPState = iota
|
||||||
|
TCPStateSynSent
|
||||||
|
TCPStateSynReceived
|
||||||
|
TCPStateEstablished
|
||||||
|
TCPStateFinWait1
|
||||||
|
TCPStateFinWait2
|
||||||
|
TCPStateClosing
|
||||||
|
TCPStateTimeWait
|
||||||
|
TCPStateCloseWait
|
||||||
|
TCPStateLastAck
|
||||||
|
TCPStateClosed
|
||||||
|
)
|
||||||
|
|
||||||
|
// TCPConnKey uniquely identifies a TCP connection
|
||||||
|
type TCPConnKey struct {
|
||||||
|
SrcIP [16]byte
|
||||||
|
DstIP [16]byte
|
||||||
|
SrcPort uint16
|
||||||
|
DstPort uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPConnTrack represents a TCP connection state
|
||||||
|
type TCPConnTrack struct {
|
||||||
|
BaseConnTrack
|
||||||
|
State TCPState
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPTracker manages TCP connection states
|
||||||
|
type TCPTracker struct {
|
||||||
|
connections map[ConnKey]*TCPConnTrack
|
||||||
|
mutex sync.RWMutex
|
||||||
|
cleanupTicker *time.Ticker
|
||||||
|
done chan struct{}
|
||||||
|
timeout time.Duration
|
||||||
|
ipPool *PreallocatedIPs
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTCPTracker creates a new TCP connection tracker
|
||||||
|
func NewTCPTracker(timeout time.Duration) *TCPTracker {
|
||||||
|
tracker := &TCPTracker{
|
||||||
|
connections: make(map[ConnKey]*TCPConnTrack),
|
||||||
|
cleanupTicker: time.NewTicker(TCPCleanupInterval),
|
||||||
|
done: make(chan struct{}),
|
||||||
|
timeout: timeout,
|
||||||
|
ipPool: NewPreallocatedIPs(),
|
||||||
|
}
|
||||||
|
|
||||||
|
go tracker.cleanupRoutine()
|
||||||
|
return tracker
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrackOutbound processes an outbound TCP packet and updates connection state
|
||||||
|
func (t *TCPTracker) TrackOutbound(srcIP net.IP, dstIP net.IP, srcPort uint16, dstPort uint16, flags uint8) {
|
||||||
|
// Create key before lock
|
||||||
|
key := makeConnKey(srcIP, dstIP, srcPort, dstPort)
|
||||||
|
now := time.Now().UnixNano()
|
||||||
|
|
||||||
|
t.mutex.Lock()
|
||||||
|
conn, exists := t.connections[key]
|
||||||
|
if !exists {
|
||||||
|
// Use preallocated IPs
|
||||||
|
srcIPCopy := t.ipPool.Get()
|
||||||
|
dstIPCopy := t.ipPool.Get()
|
||||||
|
copyIP(srcIPCopy, srcIP)
|
||||||
|
copyIP(dstIPCopy, dstIP)
|
||||||
|
|
||||||
|
conn = &TCPConnTrack{
|
||||||
|
BaseConnTrack: BaseConnTrack{
|
||||||
|
SourceIP: srcIPCopy,
|
||||||
|
DestIP: dstIPCopy,
|
||||||
|
SourcePort: srcPort,
|
||||||
|
DestPort: dstPort,
|
||||||
|
},
|
||||||
|
State: TCPStateNew,
|
||||||
|
}
|
||||||
|
conn.lastSeen.Store(now)
|
||||||
|
conn.established.Store(false)
|
||||||
|
t.connections[key] = conn
|
||||||
|
}
|
||||||
|
t.mutex.Unlock()
|
||||||
|
|
||||||
|
// Lock individual connection for state update
|
||||||
|
conn.Lock()
|
||||||
|
t.updateState(conn, flags, true)
|
||||||
|
conn.Unlock()
|
||||||
|
conn.lastSeen.Store(now)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidInbound checks if an inbound TCP packet matches a tracked connection
|
||||||
|
func (t *TCPTracker) IsValidInbound(srcIP net.IP, dstIP net.IP, srcPort uint16, dstPort uint16, flags uint8) bool {
|
||||||
|
if !isValidFlagCombination(flags) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle new SYN packets
|
||||||
|
if flags&TCPSyn != 0 && flags&TCPAck == 0 {
|
||||||
|
key := makeConnKey(dstIP, srcIP, dstPort, srcPort)
|
||||||
|
t.mutex.Lock()
|
||||||
|
if _, exists := t.connections[key]; !exists {
|
||||||
|
// Use preallocated IPs
|
||||||
|
srcIPCopy := t.ipPool.Get()
|
||||||
|
dstIPCopy := t.ipPool.Get()
|
||||||
|
copyIP(srcIPCopy, dstIP)
|
||||||
|
copyIP(dstIPCopy, srcIP)
|
||||||
|
|
||||||
|
conn := &TCPConnTrack{
|
||||||
|
BaseConnTrack: BaseConnTrack{
|
||||||
|
SourceIP: srcIPCopy,
|
||||||
|
DestIP: dstIPCopy,
|
||||||
|
SourcePort: dstPort,
|
||||||
|
DestPort: srcPort,
|
||||||
|
},
|
||||||
|
State: TCPStateSynReceived,
|
||||||
|
}
|
||||||
|
conn.lastSeen.Store(time.Now().UnixNano())
|
||||||
|
conn.established.Store(false)
|
||||||
|
t.connections[key] = conn
|
||||||
|
}
|
||||||
|
t.mutex.Unlock()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up existing connection
|
||||||
|
key := makeConnKey(dstIP, srcIP, dstPort, srcPort)
|
||||||
|
t.mutex.RLock()
|
||||||
|
conn, exists := t.connections[key]
|
||||||
|
t.mutex.RUnlock()
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle RST packets
|
||||||
|
if flags&TCPRst != 0 {
|
||||||
|
conn.Lock()
|
||||||
|
isEstablished := conn.IsEstablished()
|
||||||
|
if isEstablished || conn.State == TCPStateSynSent || conn.State == TCPStateSynReceived {
|
||||||
|
conn.State = TCPStateClosed
|
||||||
|
conn.SetEstablished(false)
|
||||||
|
conn.Unlock()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
conn.Unlock()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update state
|
||||||
|
conn.Lock()
|
||||||
|
t.updateState(conn, flags, false)
|
||||||
|
conn.UpdateLastSeen()
|
||||||
|
isEstablished := conn.IsEstablished()
|
||||||
|
isValidState := t.isValidStateForFlags(conn.State, flags)
|
||||||
|
conn.Unlock()
|
||||||
|
|
||||||
|
return isEstablished || isValidState
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateState updates the TCP connection state based on flags
|
||||||
|
func (t *TCPTracker) updateState(conn *TCPConnTrack, flags uint8, isOutbound bool) {
|
||||||
|
// Handle RST flag specially - it always causes transition to closed
|
||||||
|
if flags&TCPRst != 0 {
|
||||||
|
conn.State = TCPStateClosed
|
||||||
|
conn.SetEstablished(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch conn.State {
|
||||||
|
case TCPStateNew:
|
||||||
|
if flags&TCPSyn != 0 && flags&TCPAck == 0 {
|
||||||
|
conn.State = TCPStateSynSent
|
||||||
|
}
|
||||||
|
|
||||||
|
case TCPStateSynSent:
|
||||||
|
if flags&TCPSyn != 0 && flags&TCPAck != 0 {
|
||||||
|
if isOutbound {
|
||||||
|
conn.State = TCPStateSynReceived
|
||||||
|
} else {
|
||||||
|
// Simultaneous open
|
||||||
|
conn.State = TCPStateEstablished
|
||||||
|
conn.SetEstablished(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case TCPStateSynReceived:
|
||||||
|
if flags&TCPAck != 0 && flags&TCPSyn == 0 {
|
||||||
|
conn.State = TCPStateEstablished
|
||||||
|
conn.SetEstablished(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
case TCPStateEstablished:
|
||||||
|
if flags&TCPFin != 0 {
|
||||||
|
if isOutbound {
|
||||||
|
conn.State = TCPStateFinWait1
|
||||||
|
} else {
|
||||||
|
conn.State = TCPStateCloseWait
|
||||||
|
}
|
||||||
|
conn.SetEstablished(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
case TCPStateFinWait1:
|
||||||
|
switch {
|
||||||
|
case flags&TCPFin != 0 && flags&TCPAck != 0:
|
||||||
|
// Simultaneous close - both sides sent FIN
|
||||||
|
conn.State = TCPStateClosing
|
||||||
|
case flags&TCPFin != 0:
|
||||||
|
conn.State = TCPStateFinWait2
|
||||||
|
case flags&TCPAck != 0:
|
||||||
|
conn.State = TCPStateFinWait2
|
||||||
|
}
|
||||||
|
|
||||||
|
case TCPStateFinWait2:
|
||||||
|
if flags&TCPFin != 0 {
|
||||||
|
conn.State = TCPStateTimeWait
|
||||||
|
}
|
||||||
|
|
||||||
|
case TCPStateClosing:
|
||||||
|
if flags&TCPAck != 0 {
|
||||||
|
conn.State = TCPStateTimeWait
|
||||||
|
// Keep established = false from previous state
|
||||||
|
}
|
||||||
|
|
||||||
|
case TCPStateCloseWait:
|
||||||
|
if flags&TCPFin != 0 {
|
||||||
|
conn.State = TCPStateLastAck
|
||||||
|
}
|
||||||
|
|
||||||
|
case TCPStateLastAck:
|
||||||
|
if flags&TCPAck != 0 {
|
||||||
|
conn.State = TCPStateClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
case TCPStateTimeWait:
|
||||||
|
// Stay in TIME-WAIT for 2MSL before transitioning to closed
|
||||||
|
// This is handled by the cleanup routine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidStateForFlags checks if the TCP flags are valid for the current connection state
|
||||||
|
func (t *TCPTracker) isValidStateForFlags(state TCPState, flags uint8) bool {
|
||||||
|
if !isValidFlagCombination(flags) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch state {
|
||||||
|
case TCPStateNew:
|
||||||
|
return flags&TCPSyn != 0 && flags&TCPAck == 0
|
||||||
|
case TCPStateSynSent:
|
||||||
|
return flags&TCPSyn != 0 && flags&TCPAck != 0
|
||||||
|
case TCPStateSynReceived:
|
||||||
|
return flags&TCPAck != 0
|
||||||
|
case TCPStateEstablished:
|
||||||
|
if flags&TCPRst != 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return flags&TCPAck != 0
|
||||||
|
case TCPStateFinWait1:
|
||||||
|
return flags&TCPFin != 0 || flags&TCPAck != 0
|
||||||
|
case TCPStateFinWait2:
|
||||||
|
return flags&TCPFin != 0 || flags&TCPAck != 0
|
||||||
|
case TCPStateClosing:
|
||||||
|
// In CLOSING state, we should accept the final ACK
|
||||||
|
return flags&TCPAck != 0
|
||||||
|
case TCPStateTimeWait:
|
||||||
|
// In TIME_WAIT, we might see retransmissions
|
||||||
|
return flags&TCPAck != 0
|
||||||
|
case TCPStateCloseWait:
|
||||||
|
return flags&TCPFin != 0 || flags&TCPAck != 0
|
||||||
|
case TCPStateLastAck:
|
||||||
|
return flags&TCPAck != 0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TCPTracker) cleanupRoutine() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-t.cleanupTicker.C:
|
||||||
|
t.cleanup()
|
||||||
|
case <-t.done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TCPTracker) cleanup() {
|
||||||
|
t.mutex.Lock()
|
||||||
|
defer t.mutex.Unlock()
|
||||||
|
|
||||||
|
for key, conn := range t.connections {
|
||||||
|
var timeout time.Duration
|
||||||
|
switch {
|
||||||
|
case conn.State == TCPStateTimeWait:
|
||||||
|
timeout = TimeWaitTimeout
|
||||||
|
case conn.IsEstablished():
|
||||||
|
timeout = t.timeout
|
||||||
|
default:
|
||||||
|
timeout = TCPHandshakeTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
lastSeen := conn.GetLastSeen()
|
||||||
|
if time.Since(lastSeen) > timeout {
|
||||||
|
// Return IPs to pool
|
||||||
|
t.ipPool.Put(conn.SourceIP)
|
||||||
|
t.ipPool.Put(conn.DestIP)
|
||||||
|
delete(t.connections, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close stops the cleanup routine and releases resources
|
||||||
|
func (t *TCPTracker) Close() {
|
||||||
|
t.cleanupTicker.Stop()
|
||||||
|
close(t.done)
|
||||||
|
|
||||||
|
// Clean up all remaining IPs
|
||||||
|
t.mutex.Lock()
|
||||||
|
for _, conn := range t.connections {
|
||||||
|
t.ipPool.Put(conn.SourceIP)
|
||||||
|
t.ipPool.Put(conn.DestIP)
|
||||||
|
}
|
||||||
|
t.connections = nil
|
||||||
|
t.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidFlagCombination(flags uint8) bool {
|
||||||
|
// Invalid: SYN+FIN
|
||||||
|
if flags&TCPSyn != 0 && flags&TCPFin != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid: RST with SYN or FIN
|
||||||
|
if flags&TCPRst != 0 && (flags&TCPSyn != 0 || flags&TCPFin != 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
311
client/firewall/uspfilter/conntrack/tcp_test.go
Normal file
311
client/firewall/uspfilter/conntrack/tcp_test.go
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
package conntrack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTCPStateMachine(t *testing.T) {
|
||||||
|
tracker := NewTCPTracker(DefaultTCPTimeout)
|
||||||
|
defer tracker.Close()
|
||||||
|
|
||||||
|
srcIP := net.ParseIP("100.64.0.1")
|
||||||
|
dstIP := net.ParseIP("100.64.0.2")
|
||||||
|
srcPort := uint16(12345)
|
||||||
|
dstPort := uint16(80)
|
||||||
|
|
||||||
|
t.Run("Security Tests", func(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
flags uint8
|
||||||
|
wantDrop bool
|
||||||
|
desc string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Block unsolicited SYN-ACK",
|
||||||
|
flags: TCPSyn | TCPAck,
|
||||||
|
wantDrop: true,
|
||||||
|
desc: "Should block SYN-ACK without prior SYN",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Block invalid SYN-FIN",
|
||||||
|
flags: TCPSyn | TCPFin,
|
||||||
|
wantDrop: true,
|
||||||
|
desc: "Should block invalid SYN-FIN combination",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Block unsolicited RST",
|
||||||
|
flags: TCPRst,
|
||||||
|
wantDrop: true,
|
||||||
|
desc: "Should block RST without connection",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Block unsolicited ACK",
|
||||||
|
flags: TCPAck,
|
||||||
|
wantDrop: true,
|
||||||
|
desc: "Should block ACK without connection",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Block data without connection",
|
||||||
|
flags: TCPAck | TCPPush,
|
||||||
|
wantDrop: true,
|
||||||
|
desc: "Should block data without established connection",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
isValid := tracker.IsValidInbound(dstIP, srcIP, dstPort, srcPort, tt.flags)
|
||||||
|
require.Equal(t, !tt.wantDrop, isValid, tt.desc)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Connection Flow Tests", func(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
test func(*testing.T)
|
||||||
|
desc string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Normal Handshake",
|
||||||
|
test: func(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
// Send initial SYN
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, srcPort, dstPort, TCPSyn)
|
||||||
|
|
||||||
|
// Receive SYN-ACK
|
||||||
|
valid := tracker.IsValidInbound(dstIP, srcIP, dstPort, srcPort, TCPSyn|TCPAck)
|
||||||
|
require.True(t, valid, "SYN-ACK should be allowed")
|
||||||
|
|
||||||
|
// Send ACK
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, srcPort, dstPort, TCPAck)
|
||||||
|
|
||||||
|
// Test data transfer
|
||||||
|
valid = tracker.IsValidInbound(dstIP, srcIP, dstPort, srcPort, TCPPush|TCPAck)
|
||||||
|
require.True(t, valid, "Data should be allowed after handshake")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Normal Close",
|
||||||
|
test: func(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
// First establish connection
|
||||||
|
establishConnection(t, tracker, srcIP, dstIP, srcPort, dstPort)
|
||||||
|
|
||||||
|
// Send FIN
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, srcPort, dstPort, TCPFin|TCPAck)
|
||||||
|
|
||||||
|
// Receive ACK for FIN
|
||||||
|
valid := tracker.IsValidInbound(dstIP, srcIP, dstPort, srcPort, TCPAck)
|
||||||
|
require.True(t, valid, "ACK for FIN should be allowed")
|
||||||
|
|
||||||
|
// Receive FIN from other side
|
||||||
|
valid = tracker.IsValidInbound(dstIP, srcIP, dstPort, srcPort, TCPFin|TCPAck)
|
||||||
|
require.True(t, valid, "FIN should be allowed")
|
||||||
|
|
||||||
|
// Send final ACK
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, srcPort, dstPort, TCPAck)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "RST During Connection",
|
||||||
|
test: func(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
// First establish connection
|
||||||
|
establishConnection(t, tracker, srcIP, dstIP, srcPort, dstPort)
|
||||||
|
|
||||||
|
// Receive RST
|
||||||
|
valid := tracker.IsValidInbound(dstIP, srcIP, dstPort, srcPort, TCPRst)
|
||||||
|
require.True(t, valid, "RST should be allowed for established connection")
|
||||||
|
|
||||||
|
// Verify connection is closed
|
||||||
|
valid = tracker.IsValidInbound(dstIP, srcIP, dstPort, srcPort, TCPPush|TCPAck)
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
require.False(t, valid, "Data should be blocked after RST")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Simultaneous Close",
|
||||||
|
test: func(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
// First establish connection
|
||||||
|
establishConnection(t, tracker, srcIP, dstIP, srcPort, dstPort)
|
||||||
|
|
||||||
|
// Both sides send FIN+ACK
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, srcPort, dstPort, TCPFin|TCPAck)
|
||||||
|
valid := tracker.IsValidInbound(dstIP, srcIP, dstPort, srcPort, TCPFin|TCPAck)
|
||||||
|
require.True(t, valid, "Simultaneous FIN should be allowed")
|
||||||
|
|
||||||
|
// Both sides send final ACK
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, srcPort, dstPort, TCPAck)
|
||||||
|
valid = tracker.IsValidInbound(dstIP, srcIP, dstPort, srcPort, TCPAck)
|
||||||
|
require.True(t, valid, "Final ACKs should be allowed")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
tracker = NewTCPTracker(DefaultTCPTimeout)
|
||||||
|
tt.test(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRSTHandling(t *testing.T) {
|
||||||
|
tracker := NewTCPTracker(DefaultTCPTimeout)
|
||||||
|
defer tracker.Close()
|
||||||
|
|
||||||
|
srcIP := net.ParseIP("100.64.0.1")
|
||||||
|
dstIP := net.ParseIP("100.64.0.2")
|
||||||
|
srcPort := uint16(12345)
|
||||||
|
dstPort := uint16(80)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
setupState func()
|
||||||
|
sendRST func()
|
||||||
|
wantValid bool
|
||||||
|
desc string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "RST in established",
|
||||||
|
setupState: func() {
|
||||||
|
// Establish connection first
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, srcPort, dstPort, TCPSyn)
|
||||||
|
tracker.IsValidInbound(dstIP, srcIP, dstPort, srcPort, TCPSyn|TCPAck)
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, srcPort, dstPort, TCPAck)
|
||||||
|
},
|
||||||
|
sendRST: func() {
|
||||||
|
tracker.IsValidInbound(dstIP, srcIP, dstPort, srcPort, TCPRst)
|
||||||
|
},
|
||||||
|
wantValid: true,
|
||||||
|
desc: "Should accept RST for established connection",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "RST without connection",
|
||||||
|
setupState: func() {},
|
||||||
|
sendRST: func() {
|
||||||
|
tracker.IsValidInbound(dstIP, srcIP, dstPort, srcPort, TCPRst)
|
||||||
|
},
|
||||||
|
wantValid: false,
|
||||||
|
desc: "Should reject RST without connection",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tt.setupState()
|
||||||
|
tt.sendRST()
|
||||||
|
|
||||||
|
// Verify connection state is as expected
|
||||||
|
key := makeConnKey(srcIP, dstIP, srcPort, dstPort)
|
||||||
|
conn := tracker.connections[key]
|
||||||
|
if tt.wantValid {
|
||||||
|
require.NotNil(t, conn)
|
||||||
|
require.Equal(t, TCPStateClosed, conn.State)
|
||||||
|
require.False(t, conn.IsEstablished())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to establish a TCP connection
|
||||||
|
func establishConnection(t *testing.T, tracker *TCPTracker, srcIP, dstIP net.IP, srcPort, dstPort uint16) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, srcPort, dstPort, TCPSyn)
|
||||||
|
|
||||||
|
valid := tracker.IsValidInbound(dstIP, srcIP, dstPort, srcPort, TCPSyn|TCPAck)
|
||||||
|
require.True(t, valid, "SYN-ACK should be allowed")
|
||||||
|
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, srcPort, dstPort, TCPAck)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkTCPTracker(b *testing.B) {
|
||||||
|
b.Run("TrackOutbound", func(b *testing.B) {
|
||||||
|
tracker := NewTCPTracker(DefaultTCPTimeout)
|
||||||
|
defer tracker.Close()
|
||||||
|
|
||||||
|
srcIP := net.ParseIP("192.168.1.1")
|
||||||
|
dstIP := net.ParseIP("192.168.1.2")
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, uint16(i%65535), 80, TCPSyn)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("IsValidInbound", func(b *testing.B) {
|
||||||
|
tracker := NewTCPTracker(DefaultTCPTimeout)
|
||||||
|
defer tracker.Close()
|
||||||
|
|
||||||
|
srcIP := net.ParseIP("192.168.1.1")
|
||||||
|
dstIP := net.ParseIP("192.168.1.2")
|
||||||
|
|
||||||
|
// Pre-populate some connections
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, uint16(i), 80, TCPSyn)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
tracker.IsValidInbound(dstIP, srcIP, 80, uint16(i%1000), TCPAck)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("ConcurrentAccess", func(b *testing.B) {
|
||||||
|
tracker := NewTCPTracker(DefaultTCPTimeout)
|
||||||
|
defer tracker.Close()
|
||||||
|
|
||||||
|
srcIP := net.ParseIP("192.168.1.1")
|
||||||
|
dstIP := net.ParseIP("192.168.1.2")
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
i := 0
|
||||||
|
for pb.Next() {
|
||||||
|
if i%2 == 0 {
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, uint16(i%65535), 80, TCPSyn)
|
||||||
|
} else {
|
||||||
|
tracker.IsValidInbound(dstIP, srcIP, 80, uint16(i%65535), TCPAck)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark connection cleanup
|
||||||
|
func BenchmarkCleanup(b *testing.B) {
|
||||||
|
b.Run("TCPCleanup", func(b *testing.B) {
|
||||||
|
tracker := NewTCPTracker(100 * time.Millisecond) // Short timeout for testing
|
||||||
|
defer tracker.Close()
|
||||||
|
|
||||||
|
// Pre-populate with expired connections
|
||||||
|
srcIP := net.ParseIP("192.168.1.1")
|
||||||
|
dstIP := net.ParseIP("192.168.1.2")
|
||||||
|
for i := 0; i < 10000; i++ {
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, uint16(i), 80, TCPSyn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for connections to expire
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
tracker.cleanup()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
158
client/firewall/uspfilter/conntrack/udp.go
Normal file
158
client/firewall/uspfilter/conntrack/udp.go
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
package conntrack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultUDPTimeout is the default timeout for UDP connections
|
||||||
|
DefaultUDPTimeout = 30 * time.Second
|
||||||
|
// UDPCleanupInterval is how often we check for stale connections
|
||||||
|
UDPCleanupInterval = 15 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
// UDPConnTrack represents a UDP connection state
|
||||||
|
type UDPConnTrack struct {
|
||||||
|
BaseConnTrack
|
||||||
|
}
|
||||||
|
|
||||||
|
// UDPTracker manages UDP connection states
|
||||||
|
type UDPTracker struct {
|
||||||
|
connections map[ConnKey]*UDPConnTrack
|
||||||
|
timeout time.Duration
|
||||||
|
cleanupTicker *time.Ticker
|
||||||
|
mutex sync.RWMutex
|
||||||
|
done chan struct{}
|
||||||
|
ipPool *PreallocatedIPs
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUDPTracker creates a new UDP connection tracker
|
||||||
|
func NewUDPTracker(timeout time.Duration) *UDPTracker {
|
||||||
|
if timeout == 0 {
|
||||||
|
timeout = DefaultUDPTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker := &UDPTracker{
|
||||||
|
connections: make(map[ConnKey]*UDPConnTrack),
|
||||||
|
timeout: timeout,
|
||||||
|
cleanupTicker: time.NewTicker(UDPCleanupInterval),
|
||||||
|
done: make(chan struct{}),
|
||||||
|
ipPool: NewPreallocatedIPs(),
|
||||||
|
}
|
||||||
|
|
||||||
|
go tracker.cleanupRoutine()
|
||||||
|
return tracker
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrackOutbound records an outbound UDP connection
|
||||||
|
func (t *UDPTracker) TrackOutbound(srcIP net.IP, dstIP net.IP, srcPort uint16, dstPort uint16) {
|
||||||
|
key := makeConnKey(srcIP, dstIP, srcPort, dstPort)
|
||||||
|
now := time.Now().UnixNano()
|
||||||
|
|
||||||
|
t.mutex.Lock()
|
||||||
|
conn, exists := t.connections[key]
|
||||||
|
if !exists {
|
||||||
|
srcIPCopy := t.ipPool.Get()
|
||||||
|
dstIPCopy := t.ipPool.Get()
|
||||||
|
copyIP(srcIPCopy, srcIP)
|
||||||
|
copyIP(dstIPCopy, dstIP)
|
||||||
|
|
||||||
|
conn = &UDPConnTrack{
|
||||||
|
BaseConnTrack: BaseConnTrack{
|
||||||
|
SourceIP: srcIPCopy,
|
||||||
|
DestIP: dstIPCopy,
|
||||||
|
SourcePort: srcPort,
|
||||||
|
DestPort: dstPort,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
conn.lastSeen.Store(now)
|
||||||
|
conn.established.Store(true)
|
||||||
|
t.connections[key] = conn
|
||||||
|
}
|
||||||
|
t.mutex.Unlock()
|
||||||
|
|
||||||
|
conn.lastSeen.Store(now)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidInbound checks if an inbound packet matches a tracked connection
|
||||||
|
func (t *UDPTracker) IsValidInbound(srcIP net.IP, dstIP net.IP, srcPort uint16, dstPort uint16) bool {
|
||||||
|
key := makeConnKey(dstIP, srcIP, dstPort, srcPort)
|
||||||
|
|
||||||
|
t.mutex.RLock()
|
||||||
|
conn, exists := t.connections[key]
|
||||||
|
t.mutex.RUnlock()
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn.timeoutExceeded(t.timeout) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn.IsEstablished() &&
|
||||||
|
ValidateIPs(MakeIPAddr(srcIP), conn.DestIP) &&
|
||||||
|
ValidateIPs(MakeIPAddr(dstIP), conn.SourceIP) &&
|
||||||
|
conn.DestPort == srcPort &&
|
||||||
|
conn.SourcePort == dstPort
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanupRoutine periodically removes stale connections
|
||||||
|
func (t *UDPTracker) cleanupRoutine() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-t.cleanupTicker.C:
|
||||||
|
t.cleanup()
|
||||||
|
case <-t.done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *UDPTracker) cleanup() {
|
||||||
|
t.mutex.Lock()
|
||||||
|
defer t.mutex.Unlock()
|
||||||
|
|
||||||
|
for key, conn := range t.connections {
|
||||||
|
if conn.timeoutExceeded(t.timeout) {
|
||||||
|
t.ipPool.Put(conn.SourceIP)
|
||||||
|
t.ipPool.Put(conn.DestIP)
|
||||||
|
delete(t.connections, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close stops the cleanup routine and releases resources
|
||||||
|
func (t *UDPTracker) Close() {
|
||||||
|
t.cleanupTicker.Stop()
|
||||||
|
close(t.done)
|
||||||
|
|
||||||
|
t.mutex.Lock()
|
||||||
|
for _, conn := range t.connections {
|
||||||
|
t.ipPool.Put(conn.SourceIP)
|
||||||
|
t.ipPool.Put(conn.DestIP)
|
||||||
|
}
|
||||||
|
t.connections = nil
|
||||||
|
t.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConnection safely retrieves a connection state
|
||||||
|
func (t *UDPTracker) GetConnection(srcIP net.IP, srcPort uint16, dstIP net.IP, dstPort uint16) (*UDPConnTrack, bool) {
|
||||||
|
t.mutex.RLock()
|
||||||
|
defer t.mutex.RUnlock()
|
||||||
|
|
||||||
|
key := makeConnKey(srcIP, dstIP, srcPort, dstPort)
|
||||||
|
conn, exists := t.connections[key]
|
||||||
|
if !exists {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timeout returns the configured timeout duration for the tracker
|
||||||
|
func (t *UDPTracker) Timeout() time.Duration {
|
||||||
|
return t.timeout
|
||||||
|
}
|
243
client/firewall/uspfilter/conntrack/udp_test.go
Normal file
243
client/firewall/uspfilter/conntrack/udp_test.go
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
package conntrack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewUDPTracker(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
timeout time.Duration
|
||||||
|
wantTimeout time.Duration
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "with custom timeout",
|
||||||
|
timeout: 1 * time.Minute,
|
||||||
|
wantTimeout: 1 * time.Minute,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with zero timeout uses default",
|
||||||
|
timeout: 0,
|
||||||
|
wantTimeout: DefaultUDPTimeout,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tracker := NewUDPTracker(tt.timeout)
|
||||||
|
assert.NotNil(t, tracker)
|
||||||
|
assert.Equal(t, tt.wantTimeout, tracker.timeout)
|
||||||
|
assert.NotNil(t, tracker.connections)
|
||||||
|
assert.NotNil(t, tracker.cleanupTicker)
|
||||||
|
assert.NotNil(t, tracker.done)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUDPTracker_TrackOutbound(t *testing.T) {
|
||||||
|
tracker := NewUDPTracker(DefaultUDPTimeout)
|
||||||
|
defer tracker.Close()
|
||||||
|
|
||||||
|
srcIP := net.ParseIP("192.168.1.2")
|
||||||
|
dstIP := net.ParseIP("192.168.1.3")
|
||||||
|
srcPort := uint16(12345)
|
||||||
|
dstPort := uint16(53)
|
||||||
|
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, srcPort, dstPort)
|
||||||
|
|
||||||
|
// Verify connection was tracked
|
||||||
|
key := makeConnKey(srcIP, dstIP, srcPort, dstPort)
|
||||||
|
conn, exists := tracker.connections[key]
|
||||||
|
require.True(t, exists)
|
||||||
|
assert.True(t, conn.SourceIP.Equal(srcIP))
|
||||||
|
assert.True(t, conn.DestIP.Equal(dstIP))
|
||||||
|
assert.Equal(t, srcPort, conn.SourcePort)
|
||||||
|
assert.Equal(t, dstPort, conn.DestPort)
|
||||||
|
assert.True(t, conn.IsEstablished())
|
||||||
|
assert.WithinDuration(t, time.Now(), conn.GetLastSeen(), 1*time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUDPTracker_IsValidInbound(t *testing.T) {
|
||||||
|
tracker := NewUDPTracker(1 * time.Second)
|
||||||
|
defer tracker.Close()
|
||||||
|
|
||||||
|
srcIP := net.ParseIP("192.168.1.2")
|
||||||
|
dstIP := net.ParseIP("192.168.1.3")
|
||||||
|
srcPort := uint16(12345)
|
||||||
|
dstPort := uint16(53)
|
||||||
|
|
||||||
|
// Track outbound connection
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, srcPort, dstPort)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
srcIP net.IP
|
||||||
|
dstIP net.IP
|
||||||
|
srcPort uint16
|
||||||
|
dstPort uint16
|
||||||
|
sleep time.Duration
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid inbound response",
|
||||||
|
srcIP: dstIP, // Original destination is now source
|
||||||
|
dstIP: srcIP, // Original source is now destination
|
||||||
|
srcPort: dstPort, // Original destination port is now source
|
||||||
|
dstPort: srcPort, // Original source port is now destination
|
||||||
|
sleep: 0,
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid source IP",
|
||||||
|
srcIP: net.ParseIP("192.168.1.4"),
|
||||||
|
dstIP: srcIP,
|
||||||
|
srcPort: dstPort,
|
||||||
|
dstPort: srcPort,
|
||||||
|
sleep: 0,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid destination IP",
|
||||||
|
srcIP: dstIP,
|
||||||
|
dstIP: net.ParseIP("192.168.1.4"),
|
||||||
|
srcPort: dstPort,
|
||||||
|
dstPort: srcPort,
|
||||||
|
sleep: 0,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid source port",
|
||||||
|
srcIP: dstIP,
|
||||||
|
dstIP: srcIP,
|
||||||
|
srcPort: 54321,
|
||||||
|
dstPort: srcPort,
|
||||||
|
sleep: 0,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid destination port",
|
||||||
|
srcIP: dstIP,
|
||||||
|
dstIP: srcIP,
|
||||||
|
srcPort: dstPort,
|
||||||
|
dstPort: 54321,
|
||||||
|
sleep: 0,
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "expired connection",
|
||||||
|
srcIP: dstIP,
|
||||||
|
dstIP: srcIP,
|
||||||
|
srcPort: dstPort,
|
||||||
|
dstPort: srcPort,
|
||||||
|
sleep: 2 * time.Second, // Longer than tracker timeout
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.sleep > 0 {
|
||||||
|
time.Sleep(tt.sleep)
|
||||||
|
}
|
||||||
|
got := tracker.IsValidInbound(tt.srcIP, tt.dstIP, tt.srcPort, tt.dstPort)
|
||||||
|
assert.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUDPTracker_Cleanup(t *testing.T) {
|
||||||
|
// Use shorter intervals for testing
|
||||||
|
timeout := 50 * time.Millisecond
|
||||||
|
cleanupInterval := 25 * time.Millisecond
|
||||||
|
|
||||||
|
// Create tracker with custom cleanup interval
|
||||||
|
tracker := &UDPTracker{
|
||||||
|
connections: make(map[ConnKey]*UDPConnTrack),
|
||||||
|
timeout: timeout,
|
||||||
|
cleanupTicker: time.NewTicker(cleanupInterval),
|
||||||
|
done: make(chan struct{}),
|
||||||
|
ipPool: NewPreallocatedIPs(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start cleanup routine
|
||||||
|
go tracker.cleanupRoutine()
|
||||||
|
|
||||||
|
// Add some connections
|
||||||
|
connections := []struct {
|
||||||
|
srcIP net.IP
|
||||||
|
dstIP net.IP
|
||||||
|
srcPort uint16
|
||||||
|
dstPort uint16
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
srcIP: net.ParseIP("192.168.1.2"),
|
||||||
|
dstIP: net.ParseIP("192.168.1.3"),
|
||||||
|
srcPort: 12345,
|
||||||
|
dstPort: 53,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
srcIP: net.ParseIP("192.168.1.4"),
|
||||||
|
dstIP: net.ParseIP("192.168.1.5"),
|
||||||
|
srcPort: 12346,
|
||||||
|
dstPort: 53,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, conn := range connections {
|
||||||
|
tracker.TrackOutbound(conn.srcIP, conn.dstIP, conn.srcPort, conn.dstPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify initial connections
|
||||||
|
assert.Len(t, tracker.connections, 2)
|
||||||
|
|
||||||
|
// Wait for connection timeout and cleanup interval
|
||||||
|
time.Sleep(timeout + 2*cleanupInterval)
|
||||||
|
|
||||||
|
tracker.mutex.RLock()
|
||||||
|
connCount := len(tracker.connections)
|
||||||
|
tracker.mutex.RUnlock()
|
||||||
|
|
||||||
|
// Verify connections were cleaned up
|
||||||
|
assert.Equal(t, 0, connCount, "Expected all connections to be cleaned up")
|
||||||
|
|
||||||
|
// Properly close the tracker
|
||||||
|
tracker.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkUDPTracker(b *testing.B) {
|
||||||
|
b.Run("TrackOutbound", func(b *testing.B) {
|
||||||
|
tracker := NewUDPTracker(DefaultUDPTimeout)
|
||||||
|
defer tracker.Close()
|
||||||
|
|
||||||
|
srcIP := net.ParseIP("192.168.1.1")
|
||||||
|
dstIP := net.ParseIP("192.168.1.2")
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, uint16(i%65535), 80)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("IsValidInbound", func(b *testing.B) {
|
||||||
|
tracker := NewUDPTracker(DefaultUDPTimeout)
|
||||||
|
defer tracker.Close()
|
||||||
|
|
||||||
|
srcIP := net.ParseIP("192.168.1.1")
|
||||||
|
dstIP := net.ParseIP("192.168.1.2")
|
||||||
|
|
||||||
|
// Pre-populate some connections
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
tracker.TrackOutbound(srcIP, dstIP, uint16(i), 80)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
tracker.IsValidInbound(dstIP, srcIP, 80, uint16(i%1000))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -4,6 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/google/gopacket"
|
"github.com/google/gopacket"
|
||||||
@ -12,6 +14,7 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
firewall "github.com/netbirdio/netbird/client/firewall/manager"
|
firewall "github.com/netbirdio/netbird/client/firewall/manager"
|
||||||
|
"github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack"
|
||||||
"github.com/netbirdio/netbird/client/iface"
|
"github.com/netbirdio/netbird/client/iface"
|
||||||
"github.com/netbirdio/netbird/client/iface/device"
|
"github.com/netbirdio/netbird/client/iface/device"
|
||||||
"github.com/netbirdio/netbird/client/internal/statemanager"
|
"github.com/netbirdio/netbird/client/internal/statemanager"
|
||||||
@ -19,6 +22,8 @@ import (
|
|||||||
|
|
||||||
const layerTypeAll = 0
|
const layerTypeAll = 0
|
||||||
|
|
||||||
|
const EnvDisableConntrack = "NB_DISABLE_CONNTRACK"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errRouteNotSupported = fmt.Errorf("route not supported with userspace firewall")
|
errRouteNotSupported = fmt.Errorf("route not supported with userspace firewall")
|
||||||
)
|
)
|
||||||
@ -42,6 +47,11 @@ type Manager struct {
|
|||||||
nativeFirewall firewall.Manager
|
nativeFirewall firewall.Manager
|
||||||
|
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
|
|
||||||
|
stateful bool
|
||||||
|
udpTracker *conntrack.UDPTracker
|
||||||
|
icmpTracker *conntrack.ICMPTracker
|
||||||
|
tcpTracker *conntrack.TCPTracker
|
||||||
}
|
}
|
||||||
|
|
||||||
// decoder for packages
|
// decoder for packages
|
||||||
@ -73,6 +83,8 @@ func CreateWithNativeFirewall(iface IFaceMapper, nativeFirewall firewall.Manager
|
|||||||
}
|
}
|
||||||
|
|
||||||
func create(iface IFaceMapper) (*Manager, error) {
|
func create(iface IFaceMapper) (*Manager, error) {
|
||||||
|
disableConntrack, _ := strconv.ParseBool(os.Getenv(EnvDisableConntrack))
|
||||||
|
|
||||||
m := &Manager{
|
m := &Manager{
|
||||||
decoders: sync.Pool{
|
decoders: sync.Pool{
|
||||||
New: func() any {
|
New: func() any {
|
||||||
@ -90,6 +102,16 @@ func create(iface IFaceMapper) (*Manager, error) {
|
|||||||
outgoingRules: make(map[string]RuleSet),
|
outgoingRules: make(map[string]RuleSet),
|
||||||
incomingRules: make(map[string]RuleSet),
|
incomingRules: make(map[string]RuleSet),
|
||||||
wgIface: iface,
|
wgIface: iface,
|
||||||
|
stateful: !disableConntrack,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only initialize trackers if stateful mode is enabled
|
||||||
|
if disableConntrack {
|
||||||
|
log.Info("conntrack is disabled")
|
||||||
|
} else {
|
||||||
|
m.udpTracker = conntrack.NewUDPTracker(conntrack.DefaultUDPTimeout)
|
||||||
|
m.icmpTracker = conntrack.NewICMPTracker(conntrack.DefaultICMPTimeout)
|
||||||
|
m.tcpTracker = conntrack.NewTCPTracker(conntrack.DefaultTCPTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := iface.SetFilter(m); err != nil {
|
if err := iface.SetFilter(m); err != nil {
|
||||||
@ -249,16 +271,16 @@ func (m *Manager) Flush() error { return nil }
|
|||||||
|
|
||||||
// DropOutgoing filter outgoing packets
|
// DropOutgoing filter outgoing packets
|
||||||
func (m *Manager) DropOutgoing(packetData []byte) bool {
|
func (m *Manager) DropOutgoing(packetData []byte) bool {
|
||||||
return m.dropFilter(packetData, m.outgoingRules, false)
|
return m.processOutgoingHooks(packetData)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DropIncoming filter incoming packets
|
// DropIncoming filter incoming packets
|
||||||
func (m *Manager) DropIncoming(packetData []byte) bool {
|
func (m *Manager) DropIncoming(packetData []byte) bool {
|
||||||
return m.dropFilter(packetData, m.incomingRules, true)
|
return m.dropFilter(packetData, m.incomingRules)
|
||||||
}
|
}
|
||||||
|
|
||||||
// dropFilter implements same logic for booth direction of the traffic
|
// processOutgoingHooks processes UDP hooks for outgoing packets and tracks TCP/UDP/ICMP
|
||||||
func (m *Manager) dropFilter(packetData []byte, rules map[string]RuleSet, isIncomingPacket bool) bool {
|
func (m *Manager) processOutgoingHooks(packetData []byte) bool {
|
||||||
m.mutex.RLock()
|
m.mutex.RLock()
|
||||||
defer m.mutex.RUnlock()
|
defer m.mutex.RUnlock()
|
||||||
|
|
||||||
@ -266,61 +288,213 @@ func (m *Manager) dropFilter(packetData []byte, rules map[string]RuleSet, isInco
|
|||||||
defer m.decoders.Put(d)
|
defer m.decoders.Put(d)
|
||||||
|
|
||||||
if err := d.parser.DecodeLayers(packetData, &d.decoded); err != nil {
|
if err := d.parser.DecodeLayers(packetData, &d.decoded); err != nil {
|
||||||
log.Tracef("couldn't decode layer, err: %s", err)
|
return false
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(d.decoded) < 2 {
|
if len(d.decoded) < 2 {
|
||||||
log.Tracef("not enough levels in network packet")
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
srcIP, dstIP := m.extractIPs(d)
|
||||||
|
if srcIP == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always process UDP hooks
|
||||||
|
if d.decoded[1] == layers.LayerTypeUDP {
|
||||||
|
// Track UDP state only if enabled
|
||||||
|
if m.stateful {
|
||||||
|
m.trackUDPOutbound(d, srcIP, dstIP)
|
||||||
|
}
|
||||||
|
return m.checkUDPHooks(d, dstIP, packetData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track other protocols only if stateful mode is enabled
|
||||||
|
if m.stateful {
|
||||||
|
switch d.decoded[1] {
|
||||||
|
case layers.LayerTypeTCP:
|
||||||
|
m.trackTCPOutbound(d, srcIP, dstIP)
|
||||||
|
case layers.LayerTypeICMPv4:
|
||||||
|
m.trackICMPOutbound(d, srcIP, dstIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) extractIPs(d *decoder) (srcIP, dstIP net.IP) {
|
||||||
|
switch d.decoded[0] {
|
||||||
|
case layers.LayerTypeIPv4:
|
||||||
|
return d.ip4.SrcIP, d.ip4.DstIP
|
||||||
|
case layers.LayerTypeIPv6:
|
||||||
|
return d.ip6.SrcIP, d.ip6.DstIP
|
||||||
|
default:
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) trackTCPOutbound(d *decoder, srcIP, dstIP net.IP) {
|
||||||
|
flags := getTCPFlags(&d.tcp)
|
||||||
|
m.tcpTracker.TrackOutbound(
|
||||||
|
srcIP,
|
||||||
|
dstIP,
|
||||||
|
uint16(d.tcp.SrcPort),
|
||||||
|
uint16(d.tcp.DstPort),
|
||||||
|
flags,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTCPFlags(tcp *layers.TCP) uint8 {
|
||||||
|
var flags uint8
|
||||||
|
if tcp.SYN {
|
||||||
|
flags |= conntrack.TCPSyn
|
||||||
|
}
|
||||||
|
if tcp.ACK {
|
||||||
|
flags |= conntrack.TCPAck
|
||||||
|
}
|
||||||
|
if tcp.FIN {
|
||||||
|
flags |= conntrack.TCPFin
|
||||||
|
}
|
||||||
|
if tcp.RST {
|
||||||
|
flags |= conntrack.TCPRst
|
||||||
|
}
|
||||||
|
if tcp.PSH {
|
||||||
|
flags |= conntrack.TCPPush
|
||||||
|
}
|
||||||
|
if tcp.URG {
|
||||||
|
flags |= conntrack.TCPUrg
|
||||||
|
}
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) trackUDPOutbound(d *decoder, srcIP, dstIP net.IP) {
|
||||||
|
m.udpTracker.TrackOutbound(
|
||||||
|
srcIP,
|
||||||
|
dstIP,
|
||||||
|
uint16(d.udp.SrcPort),
|
||||||
|
uint16(d.udp.DstPort),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) checkUDPHooks(d *decoder, dstIP net.IP, packetData []byte) bool {
|
||||||
|
for _, ipKey := range []string{dstIP.String(), "0.0.0.0", "::"} {
|
||||||
|
if rules, exists := m.outgoingRules[ipKey]; exists {
|
||||||
|
for _, rule := range rules {
|
||||||
|
if rule.udpHook != nil && (rule.dPort == 0 || rule.dPort == uint16(d.udp.DstPort)) {
|
||||||
|
return rule.udpHook(packetData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) trackICMPOutbound(d *decoder, srcIP, dstIP net.IP) {
|
||||||
|
if d.icmp4.TypeCode.Type() == layers.ICMPv4TypeEchoRequest {
|
||||||
|
m.icmpTracker.TrackOutbound(
|
||||||
|
srcIP,
|
||||||
|
dstIP,
|
||||||
|
d.icmp4.Id,
|
||||||
|
d.icmp4.Seq,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dropFilter implements filtering logic for incoming packets
|
||||||
|
func (m *Manager) dropFilter(packetData []byte, rules map[string]RuleSet) bool {
|
||||||
|
m.mutex.RLock()
|
||||||
|
defer m.mutex.RUnlock()
|
||||||
|
|
||||||
|
d := m.decoders.Get().(*decoder)
|
||||||
|
defer m.decoders.Put(d)
|
||||||
|
|
||||||
|
if !m.isValidPacket(d, packetData) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
ipLayer := d.decoded[0]
|
srcIP, dstIP := m.extractIPs(d)
|
||||||
|
if srcIP == nil {
|
||||||
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])
|
log.Errorf("unknown layer: %v", d.decoded[0])
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
var ip net.IP
|
if !m.isWireguardTraffic(srcIP, dstIP) {
|
||||||
switch ipLayer {
|
return false
|
||||||
case layers.LayerTypeIPv4:
|
|
||||||
if isIncomingPacket {
|
|
||||||
ip = d.ip4.SrcIP
|
|
||||||
} else {
|
|
||||||
ip = d.ip4.DstIP
|
|
||||||
}
|
|
||||||
case layers.LayerTypeIPv6:
|
|
||||||
if isIncomingPacket {
|
|
||||||
ip = d.ip6.SrcIP
|
|
||||||
} else {
|
|
||||||
ip = d.ip6.DstIP
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
filter, ok := validateRule(ip, packetData, rules[ip.String()], d)
|
// Check connection state only if enabled
|
||||||
if ok {
|
if m.stateful && m.isValidTrackedConnection(d, srcIP, dstIP) {
|
||||||
return filter
|
return false
|
||||||
}
|
}
|
||||||
filter, ok = validateRule(ip, packetData, rules["0.0.0.0"], d)
|
|
||||||
if ok {
|
return m.applyRules(srcIP, packetData, rules, d)
|
||||||
return filter
|
}
|
||||||
|
|
||||||
|
func (m *Manager) isValidPacket(d *decoder, packetData []byte) bool {
|
||||||
|
if err := d.parser.DecodeLayers(packetData, &d.decoded); err != nil {
|
||||||
|
log.Tracef("couldn't decode layer, err: %s", err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
filter, ok = validateRule(ip, packetData, rules["::"], d)
|
|
||||||
if ok {
|
if len(d.decoded) < 2 {
|
||||||
|
log.Tracef("not enough levels in network packet")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) isWireguardTraffic(srcIP, dstIP net.IP) bool {
|
||||||
|
return m.wgNetwork.Contains(srcIP) && m.wgNetwork.Contains(dstIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) isValidTrackedConnection(d *decoder, srcIP, dstIP net.IP) bool {
|
||||||
|
switch d.decoded[1] {
|
||||||
|
case layers.LayerTypeTCP:
|
||||||
|
return m.tcpTracker.IsValidInbound(
|
||||||
|
srcIP,
|
||||||
|
dstIP,
|
||||||
|
uint16(d.tcp.SrcPort),
|
||||||
|
uint16(d.tcp.DstPort),
|
||||||
|
getTCPFlags(&d.tcp),
|
||||||
|
)
|
||||||
|
|
||||||
|
case layers.LayerTypeUDP:
|
||||||
|
return m.udpTracker.IsValidInbound(
|
||||||
|
srcIP,
|
||||||
|
dstIP,
|
||||||
|
uint16(d.udp.SrcPort),
|
||||||
|
uint16(d.udp.DstPort),
|
||||||
|
)
|
||||||
|
|
||||||
|
case layers.LayerTypeICMPv4:
|
||||||
|
return m.icmpTracker.IsValidInbound(
|
||||||
|
srcIP,
|
||||||
|
dstIP,
|
||||||
|
d.icmp4.Id,
|
||||||
|
d.icmp4.Seq,
|
||||||
|
d.icmp4.TypeCode.Type(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: ICMPv6
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) applyRules(srcIP net.IP, packetData []byte, rules map[string]RuleSet, d *decoder) bool {
|
||||||
|
if filter, ok := validateRule(srcIP, packetData, rules[srcIP.String()], d); ok {
|
||||||
return filter
|
return filter
|
||||||
}
|
}
|
||||||
|
|
||||||
// default policy is DROP ALL
|
if filter, ok := validateRule(srcIP, packetData, rules["0.0.0.0"], d); ok {
|
||||||
|
return filter
|
||||||
|
}
|
||||||
|
|
||||||
|
if filter, ok := validateRule(srcIP, packetData, rules["::"], d); ok {
|
||||||
|
return filter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default policy: DROP ALL
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
998
client/firewall/uspfilter/uspfilter_bench_test.go
Normal file
998
client/firewall/uspfilter/uspfilter_bench_test.go
Normal file
@ -0,0 +1,998 @@
|
|||||||
|
package uspfilter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
fw "github.com/netbirdio/netbird/client/firewall/manager"
|
||||||
|
"github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack"
|
||||||
|
"github.com/netbirdio/netbird/client/iface/device"
|
||||||
|
)
|
||||||
|
|
||||||
|
// generateRandomIPs generates n different random IPs in the 100.64.0.0/10 range
|
||||||
|
func generateRandomIPs(n int) []net.IP {
|
||||||
|
ips := make([]net.IP, n)
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
|
||||||
|
for i := 0; i < n; {
|
||||||
|
ip := make(net.IP, 4)
|
||||||
|
ip[0] = 100
|
||||||
|
ip[1] = byte(64 + rand.Intn(63)) // 64-126
|
||||||
|
ip[2] = byte(rand.Intn(256))
|
||||||
|
ip[3] = byte(1 + rand.Intn(254)) // avoid .0 and .255
|
||||||
|
|
||||||
|
key := ip.String()
|
||||||
|
if !seen[key] {
|
||||||
|
ips[i] = ip
|
||||||
|
seen[key] = true
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ips
|
||||||
|
}
|
||||||
|
|
||||||
|
func generatePacket(b *testing.B, srcIP, dstIP net.IP, srcPort, dstPort uint16, protocol layers.IPProtocol) []byte {
|
||||||
|
b.Helper()
|
||||||
|
|
||||||
|
ipv4 := &layers.IPv4{
|
||||||
|
TTL: 64,
|
||||||
|
Version: 4,
|
||||||
|
SrcIP: srcIP,
|
||||||
|
DstIP: dstIP,
|
||||||
|
Protocol: protocol,
|
||||||
|
}
|
||||||
|
|
||||||
|
var transportLayer gopacket.SerializableLayer
|
||||||
|
switch protocol {
|
||||||
|
case layers.IPProtocolTCP:
|
||||||
|
tcp := &layers.TCP{
|
||||||
|
SrcPort: layers.TCPPort(srcPort),
|
||||||
|
DstPort: layers.TCPPort(dstPort),
|
||||||
|
SYN: true,
|
||||||
|
}
|
||||||
|
require.NoError(b, tcp.SetNetworkLayerForChecksum(ipv4))
|
||||||
|
transportLayer = tcp
|
||||||
|
case layers.IPProtocolUDP:
|
||||||
|
udp := &layers.UDP{
|
||||||
|
SrcPort: layers.UDPPort(srcPort),
|
||||||
|
DstPort: layers.UDPPort(dstPort),
|
||||||
|
}
|
||||||
|
require.NoError(b, udp.SetNetworkLayerForChecksum(ipv4))
|
||||||
|
transportLayer = udp
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := gopacket.NewSerializeBuffer()
|
||||||
|
opts := gopacket.SerializeOptions{ComputeChecksums: true, FixLengths: true}
|
||||||
|
err := gopacket.SerializeLayers(buf, opts, ipv4, transportLayer, gopacket.Payload("test"))
|
||||||
|
require.NoError(b, err)
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkCoreFiltering focuses on the essential performance comparisons between
|
||||||
|
// stateful and stateless filtering approaches
|
||||||
|
func BenchmarkCoreFiltering(b *testing.B) {
|
||||||
|
scenarios := []struct {
|
||||||
|
name string
|
||||||
|
stateful bool
|
||||||
|
setupFunc func(*Manager)
|
||||||
|
desc string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "stateless_single_allow_all",
|
||||||
|
stateful: false,
|
||||||
|
setupFunc: func(m *Manager) {
|
||||||
|
// Single rule allowing all traffic
|
||||||
|
_, err := m.AddPeerFiltering(net.ParseIP("0.0.0.0"), fw.ProtocolALL, nil, nil,
|
||||||
|
fw.RuleDirectionIN, fw.ActionAccept, "", "allow all")
|
||||||
|
require.NoError(b, err)
|
||||||
|
},
|
||||||
|
desc: "Baseline: Single 'allow all' rule without connection tracking",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stateful_no_rules",
|
||||||
|
stateful: true,
|
||||||
|
setupFunc: func(m *Manager) {
|
||||||
|
// No explicit rules - rely purely on connection tracking
|
||||||
|
},
|
||||||
|
desc: "Pure connection tracking without any rules",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stateless_explicit_return",
|
||||||
|
stateful: false,
|
||||||
|
setupFunc: func(m *Manager) {
|
||||||
|
// Add explicit rules matching return traffic pattern
|
||||||
|
for i := 0; i < 1000; i++ { // Simulate realistic ruleset size
|
||||||
|
ip := generateRandomIPs(1)[0]
|
||||||
|
_, err := m.AddPeerFiltering(ip, fw.ProtocolTCP,
|
||||||
|
&fw.Port{Values: []int{1024 + i}},
|
||||||
|
&fw.Port{Values: []int{80}},
|
||||||
|
fw.RuleDirectionIN, fw.ActionAccept, "", "explicit return")
|
||||||
|
require.NoError(b, err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
desc: "Explicit rules matching return traffic patterns without state",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stateful_with_established",
|
||||||
|
stateful: true,
|
||||||
|
setupFunc: func(m *Manager) {
|
||||||
|
// Add some basic rules but rely on state for established connections
|
||||||
|
_, err := m.AddPeerFiltering(net.ParseIP("0.0.0.0"), fw.ProtocolTCP, nil, nil,
|
||||||
|
fw.RuleDirectionIN, fw.ActionDrop, "", "default drop")
|
||||||
|
require.NoError(b, err)
|
||||||
|
},
|
||||||
|
desc: "Connection tracking with established connections",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test both TCP and UDP
|
||||||
|
protocols := []struct {
|
||||||
|
name string
|
||||||
|
proto layers.IPProtocol
|
||||||
|
}{
|
||||||
|
{"TCP", layers.IPProtocolTCP},
|
||||||
|
{"UDP", layers.IPProtocolUDP},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sc := range scenarios {
|
||||||
|
for _, proto := range protocols {
|
||||||
|
b.Run(fmt.Sprintf("%s_%s", sc.name, proto.name), func(b *testing.B) {
|
||||||
|
// Configure stateful/stateless mode
|
||||||
|
if !sc.stateful {
|
||||||
|
require.NoError(b, os.Setenv("NB_DISABLE_CONNTRACK", "1"))
|
||||||
|
} else {
|
||||||
|
require.NoError(b, os.Setenv("NB_CONNTRACK_TIMEOUT", "1m"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create manager and basic setup
|
||||||
|
manager, _ := Create(&IFaceMock{
|
||||||
|
SetFilterFunc: func(device.PacketFilter) error { return nil },
|
||||||
|
})
|
||||||
|
defer b.Cleanup(func() {
|
||||||
|
require.NoError(b, manager.Reset(nil))
|
||||||
|
})
|
||||||
|
|
||||||
|
manager.wgNetwork = &net.IPNet{
|
||||||
|
IP: net.ParseIP("100.64.0.0"),
|
||||||
|
Mask: net.CIDRMask(10, 32),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply scenario-specific setup
|
||||||
|
sc.setupFunc(manager)
|
||||||
|
|
||||||
|
// Generate test packets
|
||||||
|
srcIP := generateRandomIPs(1)[0]
|
||||||
|
dstIP := generateRandomIPs(1)[0]
|
||||||
|
srcPort := uint16(1024 + b.N%60000)
|
||||||
|
dstPort := uint16(80)
|
||||||
|
|
||||||
|
outbound := generatePacket(b, srcIP, dstIP, srcPort, dstPort, proto.proto)
|
||||||
|
inbound := generatePacket(b, dstIP, srcIP, dstPort, srcPort, proto.proto)
|
||||||
|
|
||||||
|
// For stateful scenarios, establish the connection
|
||||||
|
if sc.stateful {
|
||||||
|
manager.processOutgoingHooks(outbound)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measure inbound packet processing
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
manager.dropFilter(inbound, manager.incomingRules)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkStateScaling measures how performance scales with connection table size
|
||||||
|
func BenchmarkStateScaling(b *testing.B) {
|
||||||
|
connCounts := []int{100, 1000, 10000, 100000}
|
||||||
|
|
||||||
|
for _, count := range connCounts {
|
||||||
|
b.Run(fmt.Sprintf("conns_%d", count), func(b *testing.B) {
|
||||||
|
manager, _ := Create(&IFaceMock{
|
||||||
|
SetFilterFunc: func(device.PacketFilter) error { return nil },
|
||||||
|
})
|
||||||
|
b.Cleanup(func() {
|
||||||
|
require.NoError(b, manager.Reset(nil))
|
||||||
|
})
|
||||||
|
|
||||||
|
manager.wgNetwork = &net.IPNet{
|
||||||
|
IP: net.ParseIP("100.64.0.0"),
|
||||||
|
Mask: net.CIDRMask(10, 32),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-populate connection table
|
||||||
|
srcIPs := generateRandomIPs(count)
|
||||||
|
dstIPs := generateRandomIPs(count)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
outbound := generatePacket(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, layers.IPProtocolTCP)
|
||||||
|
manager.processOutgoingHooks(outbound)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test packet
|
||||||
|
testOut := generatePacket(b, srcIPs[0], dstIPs[0], 1024, 80, layers.IPProtocolTCP)
|
||||||
|
testIn := generatePacket(b, dstIPs[0], srcIPs[0], 80, 1024, layers.IPProtocolTCP)
|
||||||
|
|
||||||
|
// First establish our test connection
|
||||||
|
manager.processOutgoingHooks(testOut)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
manager.dropFilter(testIn, manager.incomingRules)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkEstablishmentOverhead measures the overhead of connection establishment
|
||||||
|
func BenchmarkEstablishmentOverhead(b *testing.B) {
|
||||||
|
scenarios := []struct {
|
||||||
|
name string
|
||||||
|
established bool
|
||||||
|
}{
|
||||||
|
{"established", true},
|
||||||
|
{"new", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sc := range scenarios {
|
||||||
|
b.Run(sc.name, func(b *testing.B) {
|
||||||
|
manager, _ := Create(&IFaceMock{
|
||||||
|
SetFilterFunc: func(device.PacketFilter) error { return nil },
|
||||||
|
})
|
||||||
|
b.Cleanup(func() {
|
||||||
|
require.NoError(b, manager.Reset(nil))
|
||||||
|
})
|
||||||
|
|
||||||
|
manager.wgNetwork = &net.IPNet{
|
||||||
|
IP: net.ParseIP("100.64.0.0"),
|
||||||
|
Mask: net.CIDRMask(10, 32),
|
||||||
|
}
|
||||||
|
|
||||||
|
srcIP := generateRandomIPs(1)[0]
|
||||||
|
dstIP := generateRandomIPs(1)[0]
|
||||||
|
outbound := generatePacket(b, srcIP, dstIP, 1024, 80, layers.IPProtocolTCP)
|
||||||
|
inbound := generatePacket(b, dstIP, srcIP, 80, 1024, layers.IPProtocolTCP)
|
||||||
|
|
||||||
|
if sc.established {
|
||||||
|
manager.processOutgoingHooks(outbound)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
manager.dropFilter(inbound, manager.incomingRules)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkRoutedNetworkReturn compares approaches for handling routed network return traffic
|
||||||
|
func BenchmarkRoutedNetworkReturn(b *testing.B) {
|
||||||
|
scenarios := []struct {
|
||||||
|
name string
|
||||||
|
proto layers.IPProtocol
|
||||||
|
state string // "new", "established", "post_handshake" (TCP only)
|
||||||
|
setupFunc func(*Manager)
|
||||||
|
genPackets func(net.IP, net.IP) ([]byte, []byte) // generates appropriate packets for the scenario
|
||||||
|
desc string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "allow_non_wg_tcp_new",
|
||||||
|
proto: layers.IPProtocolTCP,
|
||||||
|
state: "new",
|
||||||
|
setupFunc: func(m *Manager) {
|
||||||
|
m.wgNetwork = &net.IPNet{
|
||||||
|
IP: net.ParseIP("100.64.0.0"),
|
||||||
|
Mask: net.CIDRMask(10, 32),
|
||||||
|
}
|
||||||
|
b.Setenv("NB_DISABLE_CONNTRACK", "1")
|
||||||
|
},
|
||||||
|
genPackets: func(srcIP, dstIP net.IP) ([]byte, []byte) {
|
||||||
|
return generatePacket(b, srcIP, dstIP, 1024, 80, layers.IPProtocolTCP),
|
||||||
|
generatePacket(b, dstIP, srcIP, 80, 1024, layers.IPProtocolTCP)
|
||||||
|
},
|
||||||
|
desc: "Allow non-WG: TCP new connection",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "allow_non_wg_tcp_established",
|
||||||
|
proto: layers.IPProtocolTCP,
|
||||||
|
state: "established",
|
||||||
|
setupFunc: func(m *Manager) {
|
||||||
|
m.wgNetwork = &net.IPNet{
|
||||||
|
IP: net.ParseIP("100.64.0.0"),
|
||||||
|
Mask: net.CIDRMask(10, 32),
|
||||||
|
}
|
||||||
|
b.Setenv("NB_DISABLE_CONNTRACK", "1")
|
||||||
|
},
|
||||||
|
genPackets: func(srcIP, dstIP net.IP) ([]byte, []byte) {
|
||||||
|
// Generate packets with ACK flag for established connection
|
||||||
|
return generateTCPPacketWithFlags(b, srcIP, dstIP, 1024, 80, uint16(conntrack.TCPAck)),
|
||||||
|
generateTCPPacketWithFlags(b, dstIP, srcIP, 80, 1024, uint16(conntrack.TCPAck))
|
||||||
|
},
|
||||||
|
desc: "Allow non-WG: TCP established connection",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "allow_non_wg_udp_new",
|
||||||
|
proto: layers.IPProtocolUDP,
|
||||||
|
state: "new",
|
||||||
|
setupFunc: func(m *Manager) {
|
||||||
|
m.wgNetwork = &net.IPNet{
|
||||||
|
IP: net.ParseIP("100.64.0.0"),
|
||||||
|
Mask: net.CIDRMask(10, 32),
|
||||||
|
}
|
||||||
|
b.Setenv("NB_DISABLE_CONNTRACK", "1")
|
||||||
|
},
|
||||||
|
genPackets: func(srcIP, dstIP net.IP) ([]byte, []byte) {
|
||||||
|
return generatePacket(b, srcIP, dstIP, 1024, 80, layers.IPProtocolUDP),
|
||||||
|
generatePacket(b, dstIP, srcIP, 80, 1024, layers.IPProtocolUDP)
|
||||||
|
},
|
||||||
|
desc: "Allow non-WG: UDP new connection",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "allow_non_wg_udp_established",
|
||||||
|
proto: layers.IPProtocolUDP,
|
||||||
|
state: "established",
|
||||||
|
setupFunc: func(m *Manager) {
|
||||||
|
m.wgNetwork = &net.IPNet{
|
||||||
|
IP: net.ParseIP("100.64.0.0"),
|
||||||
|
Mask: net.CIDRMask(10, 32),
|
||||||
|
}
|
||||||
|
b.Setenv("NB_DISABLE_CONNTRACK", "1")
|
||||||
|
},
|
||||||
|
genPackets: func(srcIP, dstIP net.IP) ([]byte, []byte) {
|
||||||
|
return generatePacket(b, srcIP, dstIP, 1024, 80, layers.IPProtocolUDP),
|
||||||
|
generatePacket(b, dstIP, srcIP, 80, 1024, layers.IPProtocolUDP)
|
||||||
|
},
|
||||||
|
desc: "Allow non-WG: UDP established connection",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stateful_tcp_new",
|
||||||
|
proto: layers.IPProtocolTCP,
|
||||||
|
state: "new",
|
||||||
|
setupFunc: func(m *Manager) {
|
||||||
|
m.wgNetwork = &net.IPNet{
|
||||||
|
IP: net.ParseIP("0.0.0.0"),
|
||||||
|
Mask: net.CIDRMask(0, 32),
|
||||||
|
}
|
||||||
|
require.NoError(b, os.Unsetenv("NB_DISABLE_CONNTRACK"))
|
||||||
|
},
|
||||||
|
genPackets: func(srcIP, dstIP net.IP) ([]byte, []byte) {
|
||||||
|
return generatePacket(b, srcIP, dstIP, 1024, 80, layers.IPProtocolTCP),
|
||||||
|
generatePacket(b, dstIP, srcIP, 80, 1024, layers.IPProtocolTCP)
|
||||||
|
},
|
||||||
|
desc: "Stateful: TCP new connection",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stateful_tcp_established",
|
||||||
|
proto: layers.IPProtocolTCP,
|
||||||
|
state: "established",
|
||||||
|
setupFunc: func(m *Manager) {
|
||||||
|
m.wgNetwork = &net.IPNet{
|
||||||
|
IP: net.ParseIP("0.0.0.0"),
|
||||||
|
Mask: net.CIDRMask(0, 32),
|
||||||
|
}
|
||||||
|
require.NoError(b, os.Unsetenv("NB_DISABLE_CONNTRACK"))
|
||||||
|
},
|
||||||
|
genPackets: func(srcIP, dstIP net.IP) ([]byte, []byte) {
|
||||||
|
// Generate established TCP packets (ACK flag)
|
||||||
|
return generateTCPPacketWithFlags(b, srcIP, dstIP, 1024, 80, uint16(conntrack.TCPAck)),
|
||||||
|
generateTCPPacketWithFlags(b, dstIP, srcIP, 80, 1024, uint16(conntrack.TCPAck))
|
||||||
|
},
|
||||||
|
desc: "Stateful: TCP established connection",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stateful_tcp_post_handshake",
|
||||||
|
proto: layers.IPProtocolTCP,
|
||||||
|
state: "post_handshake",
|
||||||
|
setupFunc: func(m *Manager) {
|
||||||
|
m.wgNetwork = &net.IPNet{
|
||||||
|
IP: net.ParseIP("0.0.0.0"),
|
||||||
|
Mask: net.CIDRMask(0, 32),
|
||||||
|
}
|
||||||
|
require.NoError(b, os.Unsetenv("NB_DISABLE_CONNTRACK"))
|
||||||
|
},
|
||||||
|
genPackets: func(srcIP, dstIP net.IP) ([]byte, []byte) {
|
||||||
|
// Generate packets with PSH+ACK flags for data transfer
|
||||||
|
return generateTCPPacketWithFlags(b, srcIP, dstIP, 1024, 80, uint16(conntrack.TCPPush|conntrack.TCPAck)),
|
||||||
|
generateTCPPacketWithFlags(b, dstIP, srcIP, 80, 1024, uint16(conntrack.TCPPush|conntrack.TCPAck))
|
||||||
|
},
|
||||||
|
desc: "Stateful: TCP post-handshake data transfer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stateful_udp_new",
|
||||||
|
proto: layers.IPProtocolUDP,
|
||||||
|
state: "new",
|
||||||
|
setupFunc: func(m *Manager) {
|
||||||
|
m.wgNetwork = &net.IPNet{
|
||||||
|
IP: net.ParseIP("0.0.0.0"),
|
||||||
|
Mask: net.CIDRMask(0, 32),
|
||||||
|
}
|
||||||
|
require.NoError(b, os.Unsetenv("NB_DISABLE_CONNTRACK"))
|
||||||
|
},
|
||||||
|
genPackets: func(srcIP, dstIP net.IP) ([]byte, []byte) {
|
||||||
|
return generatePacket(b, srcIP, dstIP, 1024, 80, layers.IPProtocolUDP),
|
||||||
|
generatePacket(b, dstIP, srcIP, 80, 1024, layers.IPProtocolUDP)
|
||||||
|
},
|
||||||
|
desc: "Stateful: UDP new connection",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stateful_udp_established",
|
||||||
|
proto: layers.IPProtocolUDP,
|
||||||
|
state: "established",
|
||||||
|
setupFunc: func(m *Manager) {
|
||||||
|
m.wgNetwork = &net.IPNet{
|
||||||
|
IP: net.ParseIP("0.0.0.0"),
|
||||||
|
Mask: net.CIDRMask(0, 32),
|
||||||
|
}
|
||||||
|
require.NoError(b, os.Unsetenv("NB_DISABLE_CONNTRACK"))
|
||||||
|
},
|
||||||
|
genPackets: func(srcIP, dstIP net.IP) ([]byte, []byte) {
|
||||||
|
return generatePacket(b, srcIP, dstIP, 1024, 80, layers.IPProtocolUDP),
|
||||||
|
generatePacket(b, dstIP, srcIP, 80, 1024, layers.IPProtocolUDP)
|
||||||
|
},
|
||||||
|
desc: "Stateful: UDP established connection",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sc := range scenarios {
|
||||||
|
b.Run(sc.name, func(b *testing.B) {
|
||||||
|
manager, _ := Create(&IFaceMock{
|
||||||
|
SetFilterFunc: func(device.PacketFilter) error { return nil },
|
||||||
|
})
|
||||||
|
b.Cleanup(func() {
|
||||||
|
require.NoError(b, manager.Reset(nil))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Setup scenario
|
||||||
|
sc.setupFunc(manager)
|
||||||
|
|
||||||
|
// Use IPs outside WG range for routed network simulation
|
||||||
|
srcIP := net.ParseIP("192.168.1.2")
|
||||||
|
dstIP := net.ParseIP("8.8.8.8")
|
||||||
|
outbound, inbound := sc.genPackets(srcIP, dstIP)
|
||||||
|
|
||||||
|
// For stateful cases and established connections
|
||||||
|
if !strings.Contains(sc.name, "allow_non_wg") ||
|
||||||
|
(strings.Contains(sc.state, "established") || sc.state == "post_handshake") {
|
||||||
|
manager.processOutgoingHooks(outbound)
|
||||||
|
|
||||||
|
// For TCP post-handshake, simulate full handshake
|
||||||
|
if sc.state == "post_handshake" {
|
||||||
|
// SYN
|
||||||
|
syn := generateTCPPacketWithFlags(b, srcIP, dstIP, 1024, 80, uint16(conntrack.TCPSyn))
|
||||||
|
manager.processOutgoingHooks(syn)
|
||||||
|
// SYN-ACK
|
||||||
|
synack := generateTCPPacketWithFlags(b, dstIP, srcIP, 80, 1024, uint16(conntrack.TCPSyn|conntrack.TCPAck))
|
||||||
|
manager.dropFilter(synack, manager.incomingRules)
|
||||||
|
// ACK
|
||||||
|
ack := generateTCPPacketWithFlags(b, srcIP, dstIP, 1024, 80, uint16(conntrack.TCPAck))
|
||||||
|
manager.processOutgoingHooks(ack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
manager.dropFilter(inbound, manager.incomingRules)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var scenarios = []struct {
|
||||||
|
name string
|
||||||
|
stateful bool // Whether conntrack is enabled
|
||||||
|
rules bool // Whether to add return traffic rules
|
||||||
|
routed bool // Whether to test routed network traffic
|
||||||
|
connCount int // Number of concurrent connections
|
||||||
|
desc string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "stateless_with_rules_100conns",
|
||||||
|
stateful: false,
|
||||||
|
rules: true,
|
||||||
|
routed: false,
|
||||||
|
connCount: 100,
|
||||||
|
desc: "Pure stateless with return traffic rules, 100 conns",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stateless_with_rules_1000conns",
|
||||||
|
stateful: false,
|
||||||
|
rules: true,
|
||||||
|
routed: false,
|
||||||
|
connCount: 1000,
|
||||||
|
desc: "Pure stateless with return traffic rules, 1000 conns",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stateful_no_rules_100conns",
|
||||||
|
stateful: true,
|
||||||
|
rules: false,
|
||||||
|
routed: false,
|
||||||
|
connCount: 100,
|
||||||
|
desc: "Pure stateful tracking without rules, 100 conns",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stateful_no_rules_1000conns",
|
||||||
|
stateful: true,
|
||||||
|
rules: false,
|
||||||
|
routed: false,
|
||||||
|
connCount: 1000,
|
||||||
|
desc: "Pure stateful tracking without rules, 1000 conns",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stateful_with_rules_100conns",
|
||||||
|
stateful: true,
|
||||||
|
rules: true,
|
||||||
|
routed: false,
|
||||||
|
connCount: 100,
|
||||||
|
desc: "Combined stateful + rules (current implementation), 100 conns",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stateful_with_rules_1000conns",
|
||||||
|
stateful: true,
|
||||||
|
rules: true,
|
||||||
|
routed: false,
|
||||||
|
connCount: 1000,
|
||||||
|
desc: "Combined stateful + rules (current implementation), 1000 conns",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "routed_network_100conns",
|
||||||
|
stateful: true,
|
||||||
|
rules: false,
|
||||||
|
routed: true,
|
||||||
|
connCount: 100,
|
||||||
|
desc: "Routed network traffic (non-WG), 100 conns",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "routed_network_1000conns",
|
||||||
|
stateful: true,
|
||||||
|
rules: false,
|
||||||
|
routed: true,
|
||||||
|
connCount: 1000,
|
||||||
|
desc: "Routed network traffic (non-WG), 1000 conns",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkLongLivedConnections tests performance with realistic TCP traffic patterns
|
||||||
|
func BenchmarkLongLivedConnections(b *testing.B) {
|
||||||
|
for _, sc := range scenarios {
|
||||||
|
b.Run(sc.name, func(b *testing.B) {
|
||||||
|
// Configure stateful/stateless mode
|
||||||
|
if !sc.stateful {
|
||||||
|
b.Setenv("NB_DISABLE_CONNTRACK", "1")
|
||||||
|
} else {
|
||||||
|
require.NoError(b, os.Unsetenv("NB_DISABLE_CONNTRACK"))
|
||||||
|
}
|
||||||
|
|
||||||
|
manager, _ := Create(&IFaceMock{
|
||||||
|
SetFilterFunc: func(device.PacketFilter) error { return nil },
|
||||||
|
})
|
||||||
|
defer b.Cleanup(func() {
|
||||||
|
require.NoError(b, manager.Reset(nil))
|
||||||
|
})
|
||||||
|
|
||||||
|
manager.SetNetwork(&net.IPNet{
|
||||||
|
IP: net.ParseIP("100.64.0.0"),
|
||||||
|
Mask: net.CIDRMask(10, 32),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Setup initial state based on scenario
|
||||||
|
if sc.rules {
|
||||||
|
// Single rule to allow all return traffic from port 80
|
||||||
|
_, err := manager.AddPeerFiltering(net.ParseIP("0.0.0.0"), fw.ProtocolTCP,
|
||||||
|
&fw.Port{Values: []int{80}},
|
||||||
|
nil,
|
||||||
|
fw.RuleDirectionIN, fw.ActionAccept, "", "return traffic")
|
||||||
|
require.NoError(b, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate IPs for connections
|
||||||
|
srcIPs := make([]net.IP, sc.connCount)
|
||||||
|
dstIPs := make([]net.IP, sc.connCount)
|
||||||
|
|
||||||
|
for i := 0; i < sc.connCount; i++ {
|
||||||
|
if sc.routed {
|
||||||
|
srcIPs[i] = net.IPv4(192, 168, 1, byte(2+(i%250))).To4()
|
||||||
|
dstIPs[i] = net.IPv4(8, 8, byte((i/250)%255), byte(2+(i%250))).To4()
|
||||||
|
} else {
|
||||||
|
srcIPs[i] = generateRandomIPs(1)[0]
|
||||||
|
dstIPs[i] = generateRandomIPs(1)[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create established connections
|
||||||
|
for i := 0; i < sc.connCount; i++ {
|
||||||
|
// Initial SYN
|
||||||
|
syn := generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPSyn))
|
||||||
|
manager.processOutgoingHooks(syn)
|
||||||
|
|
||||||
|
// SYN-ACK
|
||||||
|
synack := generateTCPPacketWithFlags(b, dstIPs[i], srcIPs[i],
|
||||||
|
80, uint16(1024+i), uint16(conntrack.TCPSyn|conntrack.TCPAck))
|
||||||
|
manager.dropFilter(synack, manager.incomingRules)
|
||||||
|
|
||||||
|
// ACK
|
||||||
|
ack := generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPAck))
|
||||||
|
manager.processOutgoingHooks(ack)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare test packets simulating bidirectional traffic
|
||||||
|
inPackets := make([][]byte, sc.connCount)
|
||||||
|
outPackets := make([][]byte, sc.connCount)
|
||||||
|
for i := 0; i < sc.connCount; i++ {
|
||||||
|
// Server -> Client (inbound)
|
||||||
|
inPackets[i] = generateTCPPacketWithFlags(b, dstIPs[i], srcIPs[i],
|
||||||
|
80, uint16(1024+i), uint16(conntrack.TCPPush|conntrack.TCPAck))
|
||||||
|
// Client -> Server (outbound)
|
||||||
|
outPackets[i] = generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPPush|conntrack.TCPAck))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
connIdx := i % sc.connCount
|
||||||
|
|
||||||
|
// Simulate bidirectional traffic
|
||||||
|
// First outbound data
|
||||||
|
manager.processOutgoingHooks(outPackets[connIdx])
|
||||||
|
// Then inbound response - this is what we're actually measuring
|
||||||
|
manager.dropFilter(inPackets[connIdx], manager.incomingRules)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkShortLivedConnections tests performance with many short-lived connections
|
||||||
|
func BenchmarkShortLivedConnections(b *testing.B) {
|
||||||
|
for _, sc := range scenarios {
|
||||||
|
b.Run(sc.name, func(b *testing.B) {
|
||||||
|
// Configure stateful/stateless mode
|
||||||
|
if !sc.stateful {
|
||||||
|
b.Setenv("NB_DISABLE_CONNTRACK", "1")
|
||||||
|
} else {
|
||||||
|
require.NoError(b, os.Unsetenv("NB_DISABLE_CONNTRACK"))
|
||||||
|
}
|
||||||
|
|
||||||
|
manager, _ := Create(&IFaceMock{
|
||||||
|
SetFilterFunc: func(device.PacketFilter) error { return nil },
|
||||||
|
})
|
||||||
|
defer b.Cleanup(func() {
|
||||||
|
require.NoError(b, manager.Reset(nil))
|
||||||
|
})
|
||||||
|
|
||||||
|
manager.SetNetwork(&net.IPNet{
|
||||||
|
IP: net.ParseIP("100.64.0.0"),
|
||||||
|
Mask: net.CIDRMask(10, 32),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Setup initial state based on scenario
|
||||||
|
if sc.rules {
|
||||||
|
// Single rule to allow all return traffic from port 80
|
||||||
|
_, err := manager.AddPeerFiltering(net.ParseIP("0.0.0.0"), fw.ProtocolTCP,
|
||||||
|
&fw.Port{Values: []int{80}},
|
||||||
|
nil,
|
||||||
|
fw.RuleDirectionIN, fw.ActionAccept, "", "return traffic")
|
||||||
|
require.NoError(b, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate IPs for connections
|
||||||
|
srcIPs := make([]net.IP, sc.connCount)
|
||||||
|
dstIPs := make([]net.IP, sc.connCount)
|
||||||
|
|
||||||
|
for i := 0; i < sc.connCount; i++ {
|
||||||
|
if sc.routed {
|
||||||
|
srcIPs[i] = net.IPv4(192, 168, 1, byte(2+(i%250))).To4()
|
||||||
|
dstIPs[i] = net.IPv4(8, 8, byte((i/250)%255), byte(2+(i%250))).To4()
|
||||||
|
} else {
|
||||||
|
srcIPs[i] = generateRandomIPs(1)[0]
|
||||||
|
dstIPs[i] = generateRandomIPs(1)[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create packet patterns for a complete HTTP-like short connection:
|
||||||
|
// 1. Initial handshake (SYN, SYN-ACK, ACK)
|
||||||
|
// 2. HTTP Request (PSH+ACK from client)
|
||||||
|
// 3. HTTP Response (PSH+ACK from server)
|
||||||
|
// 4. Connection teardown (FIN+ACK, ACK, FIN+ACK, ACK)
|
||||||
|
type connPackets struct {
|
||||||
|
syn []byte
|
||||||
|
synAck []byte
|
||||||
|
ack []byte
|
||||||
|
request []byte
|
||||||
|
response []byte
|
||||||
|
finClient []byte
|
||||||
|
ackServer []byte
|
||||||
|
finServer []byte
|
||||||
|
ackClient []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate all possible connection patterns
|
||||||
|
patterns := make([]connPackets, sc.connCount)
|
||||||
|
for i := 0; i < sc.connCount; i++ {
|
||||||
|
patterns[i] = connPackets{
|
||||||
|
// Handshake
|
||||||
|
syn: generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPSyn)),
|
||||||
|
synAck: generateTCPPacketWithFlags(b, dstIPs[i], srcIPs[i],
|
||||||
|
80, uint16(1024+i), uint16(conntrack.TCPSyn|conntrack.TCPAck)),
|
||||||
|
ack: generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPAck)),
|
||||||
|
|
||||||
|
// Data transfer
|
||||||
|
request: generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPPush|conntrack.TCPAck)),
|
||||||
|
response: generateTCPPacketWithFlags(b, dstIPs[i], srcIPs[i],
|
||||||
|
80, uint16(1024+i), uint16(conntrack.TCPPush|conntrack.TCPAck)),
|
||||||
|
|
||||||
|
// Connection teardown
|
||||||
|
finClient: generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPFin|conntrack.TCPAck)),
|
||||||
|
ackServer: generateTCPPacketWithFlags(b, dstIPs[i], srcIPs[i],
|
||||||
|
80, uint16(1024+i), uint16(conntrack.TCPAck)),
|
||||||
|
finServer: generateTCPPacketWithFlags(b, dstIPs[i], srcIPs[i],
|
||||||
|
80, uint16(1024+i), uint16(conntrack.TCPFin|conntrack.TCPAck)),
|
||||||
|
ackClient: generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPAck)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
// Each iteration creates a new short-lived connection
|
||||||
|
connIdx := i % sc.connCount
|
||||||
|
p := patterns[connIdx]
|
||||||
|
|
||||||
|
// Connection establishment
|
||||||
|
manager.processOutgoingHooks(p.syn)
|
||||||
|
manager.dropFilter(p.synAck, manager.incomingRules)
|
||||||
|
manager.processOutgoingHooks(p.ack)
|
||||||
|
|
||||||
|
// Data transfer
|
||||||
|
manager.processOutgoingHooks(p.request)
|
||||||
|
manager.dropFilter(p.response, manager.incomingRules)
|
||||||
|
|
||||||
|
// Connection teardown
|
||||||
|
manager.processOutgoingHooks(p.finClient)
|
||||||
|
manager.dropFilter(p.ackServer, manager.incomingRules)
|
||||||
|
manager.dropFilter(p.finServer, manager.incomingRules)
|
||||||
|
manager.processOutgoingHooks(p.ackClient)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkParallelLongLivedConnections tests performance with realistic TCP traffic patterns in parallel
|
||||||
|
func BenchmarkParallelLongLivedConnections(b *testing.B) {
|
||||||
|
for _, sc := range scenarios {
|
||||||
|
b.Run(sc.name, func(b *testing.B) {
|
||||||
|
// Configure stateful/stateless mode
|
||||||
|
if !sc.stateful {
|
||||||
|
b.Setenv("NB_DISABLE_CONNTRACK", "1")
|
||||||
|
} else {
|
||||||
|
require.NoError(b, os.Unsetenv("NB_DISABLE_CONNTRACK"))
|
||||||
|
}
|
||||||
|
|
||||||
|
manager, _ := Create(&IFaceMock{
|
||||||
|
SetFilterFunc: func(device.PacketFilter) error { return nil },
|
||||||
|
})
|
||||||
|
defer b.Cleanup(func() {
|
||||||
|
require.NoError(b, manager.Reset(nil))
|
||||||
|
})
|
||||||
|
|
||||||
|
manager.SetNetwork(&net.IPNet{
|
||||||
|
IP: net.ParseIP("100.64.0.0"),
|
||||||
|
Mask: net.CIDRMask(10, 32),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Setup initial state based on scenario
|
||||||
|
if sc.rules {
|
||||||
|
_, err := manager.AddPeerFiltering(net.ParseIP("0.0.0.0"), fw.ProtocolTCP,
|
||||||
|
&fw.Port{Values: []int{80}},
|
||||||
|
nil,
|
||||||
|
fw.RuleDirectionIN, fw.ActionAccept, "", "return traffic")
|
||||||
|
require.NoError(b, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate IPs for connections
|
||||||
|
srcIPs := make([]net.IP, sc.connCount)
|
||||||
|
dstIPs := make([]net.IP, sc.connCount)
|
||||||
|
|
||||||
|
for i := 0; i < sc.connCount; i++ {
|
||||||
|
if sc.routed {
|
||||||
|
srcIPs[i] = net.IPv4(192, 168, 1, byte(2+(i%250))).To4()
|
||||||
|
dstIPs[i] = net.IPv4(8, 8, byte((i/250)%255), byte(2+(i%250))).To4()
|
||||||
|
} else {
|
||||||
|
srcIPs[i] = generateRandomIPs(1)[0]
|
||||||
|
dstIPs[i] = generateRandomIPs(1)[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create established connections
|
||||||
|
for i := 0; i < sc.connCount; i++ {
|
||||||
|
syn := generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPSyn))
|
||||||
|
manager.processOutgoingHooks(syn)
|
||||||
|
|
||||||
|
synack := generateTCPPacketWithFlags(b, dstIPs[i], srcIPs[i],
|
||||||
|
80, uint16(1024+i), uint16(conntrack.TCPSyn|conntrack.TCPAck))
|
||||||
|
manager.dropFilter(synack, manager.incomingRules)
|
||||||
|
|
||||||
|
ack := generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPAck))
|
||||||
|
manager.processOutgoingHooks(ack)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-generate test packets
|
||||||
|
inPackets := make([][]byte, sc.connCount)
|
||||||
|
outPackets := make([][]byte, sc.connCount)
|
||||||
|
for i := 0; i < sc.connCount; i++ {
|
||||||
|
inPackets[i] = generateTCPPacketWithFlags(b, dstIPs[i], srcIPs[i],
|
||||||
|
80, uint16(1024+i), uint16(conntrack.TCPPush|conntrack.TCPAck))
|
||||||
|
outPackets[i] = generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPPush|conntrack.TCPAck))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
// Each goroutine gets its own counter to distribute load
|
||||||
|
counter := 0
|
||||||
|
for pb.Next() {
|
||||||
|
connIdx := counter % sc.connCount
|
||||||
|
counter++
|
||||||
|
|
||||||
|
// Simulate bidirectional traffic
|
||||||
|
manager.processOutgoingHooks(outPackets[connIdx])
|
||||||
|
manager.dropFilter(inPackets[connIdx], manager.incomingRules)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkParallelShortLivedConnections tests performance with many short-lived connections in parallel
|
||||||
|
func BenchmarkParallelShortLivedConnections(b *testing.B) {
|
||||||
|
for _, sc := range scenarios {
|
||||||
|
b.Run(sc.name, func(b *testing.B) {
|
||||||
|
// Configure stateful/stateless mode
|
||||||
|
if !sc.stateful {
|
||||||
|
b.Setenv("NB_DISABLE_CONNTRACK", "1")
|
||||||
|
} else {
|
||||||
|
require.NoError(b, os.Unsetenv("NB_DISABLE_CONNTRACK"))
|
||||||
|
}
|
||||||
|
|
||||||
|
manager, _ := Create(&IFaceMock{
|
||||||
|
SetFilterFunc: func(device.PacketFilter) error { return nil },
|
||||||
|
})
|
||||||
|
defer b.Cleanup(func() {
|
||||||
|
require.NoError(b, manager.Reset(nil))
|
||||||
|
})
|
||||||
|
|
||||||
|
manager.SetNetwork(&net.IPNet{
|
||||||
|
IP: net.ParseIP("100.64.0.0"),
|
||||||
|
Mask: net.CIDRMask(10, 32),
|
||||||
|
})
|
||||||
|
|
||||||
|
if sc.rules {
|
||||||
|
_, err := manager.AddPeerFiltering(net.ParseIP("0.0.0.0"), fw.ProtocolTCP,
|
||||||
|
&fw.Port{Values: []int{80}},
|
||||||
|
nil,
|
||||||
|
fw.RuleDirectionIN, fw.ActionAccept, "", "return traffic")
|
||||||
|
require.NoError(b, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate IPs and pre-generate all packet patterns
|
||||||
|
srcIPs := make([]net.IP, sc.connCount)
|
||||||
|
dstIPs := make([]net.IP, sc.connCount)
|
||||||
|
for i := 0; i < sc.connCount; i++ {
|
||||||
|
if sc.routed {
|
||||||
|
srcIPs[i] = net.IPv4(192, 168, 1, byte(2+(i%250))).To4()
|
||||||
|
dstIPs[i] = net.IPv4(8, 8, byte((i/250)%255), byte(2+(i%250))).To4()
|
||||||
|
} else {
|
||||||
|
srcIPs[i] = generateRandomIPs(1)[0]
|
||||||
|
dstIPs[i] = generateRandomIPs(1)[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type connPackets struct {
|
||||||
|
syn []byte
|
||||||
|
synAck []byte
|
||||||
|
ack []byte
|
||||||
|
request []byte
|
||||||
|
response []byte
|
||||||
|
finClient []byte
|
||||||
|
ackServer []byte
|
||||||
|
finServer []byte
|
||||||
|
ackClient []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
patterns := make([]connPackets, sc.connCount)
|
||||||
|
for i := 0; i < sc.connCount; i++ {
|
||||||
|
patterns[i] = connPackets{
|
||||||
|
syn: generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPSyn)),
|
||||||
|
synAck: generateTCPPacketWithFlags(b, dstIPs[i], srcIPs[i],
|
||||||
|
80, uint16(1024+i), uint16(conntrack.TCPSyn|conntrack.TCPAck)),
|
||||||
|
ack: generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPAck)),
|
||||||
|
request: generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPPush|conntrack.TCPAck)),
|
||||||
|
response: generateTCPPacketWithFlags(b, dstIPs[i], srcIPs[i],
|
||||||
|
80, uint16(1024+i), uint16(conntrack.TCPPush|conntrack.TCPAck)),
|
||||||
|
finClient: generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPFin|conntrack.TCPAck)),
|
||||||
|
ackServer: generateTCPPacketWithFlags(b, dstIPs[i], srcIPs[i],
|
||||||
|
80, uint16(1024+i), uint16(conntrack.TCPAck)),
|
||||||
|
finServer: generateTCPPacketWithFlags(b, dstIPs[i], srcIPs[i],
|
||||||
|
80, uint16(1024+i), uint16(conntrack.TCPFin|conntrack.TCPAck)),
|
||||||
|
ackClient: generateTCPPacketWithFlags(b, srcIPs[i], dstIPs[i],
|
||||||
|
uint16(1024+i), 80, uint16(conntrack.TCPAck)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
counter := 0
|
||||||
|
for pb.Next() {
|
||||||
|
connIdx := counter % sc.connCount
|
||||||
|
counter++
|
||||||
|
p := patterns[connIdx]
|
||||||
|
|
||||||
|
// Full connection lifecycle
|
||||||
|
manager.processOutgoingHooks(p.syn)
|
||||||
|
manager.dropFilter(p.synAck, manager.incomingRules)
|
||||||
|
manager.processOutgoingHooks(p.ack)
|
||||||
|
|
||||||
|
manager.processOutgoingHooks(p.request)
|
||||||
|
manager.dropFilter(p.response, manager.incomingRules)
|
||||||
|
|
||||||
|
manager.processOutgoingHooks(p.finClient)
|
||||||
|
manager.dropFilter(p.ackServer, manager.incomingRules)
|
||||||
|
manager.dropFilter(p.finServer, manager.incomingRules)
|
||||||
|
manager.processOutgoingHooks(p.ackClient)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateTCPPacketWithFlags creates a TCP packet with specific flags
|
||||||
|
func generateTCPPacketWithFlags(b *testing.B, srcIP, dstIP net.IP, srcPort, dstPort, flags uint16) []byte {
|
||||||
|
b.Helper()
|
||||||
|
|
||||||
|
ipv4 := &layers.IPv4{
|
||||||
|
TTL: 64,
|
||||||
|
Version: 4,
|
||||||
|
SrcIP: srcIP,
|
||||||
|
DstIP: dstIP,
|
||||||
|
Protocol: layers.IPProtocolTCP,
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp := &layers.TCP{
|
||||||
|
SrcPort: layers.TCPPort(srcPort),
|
||||||
|
DstPort: layers.TCPPort(dstPort),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set TCP flags
|
||||||
|
tcp.SYN = (flags & uint16(conntrack.TCPSyn)) != 0
|
||||||
|
tcp.ACK = (flags & uint16(conntrack.TCPAck)) != 0
|
||||||
|
tcp.PSH = (flags & uint16(conntrack.TCPPush)) != 0
|
||||||
|
tcp.RST = (flags & uint16(conntrack.TCPRst)) != 0
|
||||||
|
tcp.FIN = (flags & uint16(conntrack.TCPFin)) != 0
|
||||||
|
|
||||||
|
require.NoError(b, tcp.SetNetworkLayerForChecksum(ipv4))
|
||||||
|
|
||||||
|
buf := gopacket.NewSerializeBuffer()
|
||||||
|
opts := gopacket.SerializeOptions{ComputeChecksums: true, FixLengths: true}
|
||||||
|
require.NoError(b, gopacket.SerializeLayers(buf, opts, ipv4, tcp, gopacket.Payload("test")))
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
@ -3,6 +3,7 @@ package uspfilter
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -11,6 +12,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
fw "github.com/netbirdio/netbird/client/firewall/manager"
|
fw "github.com/netbirdio/netbird/client/firewall/manager"
|
||||||
|
"github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack"
|
||||||
"github.com/netbirdio/netbird/client/iface"
|
"github.com/netbirdio/netbird/client/iface"
|
||||||
"github.com/netbirdio/netbird/client/iface/device"
|
"github.com/netbirdio/netbird/client/iface/device"
|
||||||
)
|
)
|
||||||
@ -185,10 +187,10 @@ func TestAddUDPPacketHook(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
manager := &Manager{
|
manager, err := Create(&IFaceMock{
|
||||||
incomingRules: map[string]RuleSet{},
|
SetFilterFunc: func(device.PacketFilter) error { return nil },
|
||||||
outgoingRules: map[string]RuleSet{},
|
})
|
||||||
}
|
require.NoError(t, err)
|
||||||
|
|
||||||
manager.AddUDPPacketHook(tt.in, tt.ip, tt.dPort, tt.hook)
|
manager.AddUDPPacketHook(tt.in, tt.ip, tt.dPort, tt.hook)
|
||||||
|
|
||||||
@ -313,7 +315,7 @@ func TestNotMatchByIP(t *testing.T) {
|
|||||||
t.Errorf("failed to set network layer for checksum: %v", err)
|
t.Errorf("failed to set network layer for checksum: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
payload := gopacket.Payload([]byte("test"))
|
payload := gopacket.Payload("test")
|
||||||
|
|
||||||
buf := gopacket.NewSerializeBuffer()
|
buf := gopacket.NewSerializeBuffer()
|
||||||
opts := gopacket.SerializeOptions{
|
opts := gopacket.SerializeOptions{
|
||||||
@ -325,7 +327,7 @@ func TestNotMatchByIP(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.dropFilter(buf.Bytes(), m.outgoingRules, false) {
|
if m.dropFilter(buf.Bytes(), m.outgoingRules) {
|
||||||
t.Errorf("expected packet to be accepted")
|
t.Errorf("expected packet to be accepted")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -348,6 +350,9 @@ func TestRemovePacketHook(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create Manager: %s", err)
|
t.Fatalf("Failed to create Manager: %s", err)
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, manager.Reset(nil))
|
||||||
|
}()
|
||||||
|
|
||||||
// Add a UDP packet hook
|
// Add a UDP packet hook
|
||||||
hookFunc := func(data []byte) bool { return true }
|
hookFunc := func(data []byte) bool { return true }
|
||||||
@ -384,6 +389,88 @@ func TestRemovePacketHook(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProcessOutgoingHooks(t *testing.T) {
|
||||||
|
manager, err := Create(&IFaceMock{
|
||||||
|
SetFilterFunc: func(device.PacketFilter) error { return nil },
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
manager.wgNetwork = &net.IPNet{
|
||||||
|
IP: net.ParseIP("100.10.0.0"),
|
||||||
|
Mask: net.CIDRMask(16, 32),
|
||||||
|
}
|
||||||
|
manager.udpTracker.Close()
|
||||||
|
manager.udpTracker = conntrack.NewUDPTracker(100 * time.Millisecond)
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, manager.Reset(nil))
|
||||||
|
}()
|
||||||
|
|
||||||
|
manager.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
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
hookCalled := false
|
||||||
|
hookID := manager.AddUDPPacketHook(
|
||||||
|
false,
|
||||||
|
net.ParseIP("100.10.0.100"),
|
||||||
|
53,
|
||||||
|
func([]byte) bool {
|
||||||
|
hookCalled = true
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
)
|
||||||
|
require.NotEmpty(t, hookID)
|
||||||
|
|
||||||
|
// Create test UDP packet
|
||||||
|
ipv4 := &layers.IPv4{
|
||||||
|
TTL: 64,
|
||||||
|
Version: 4,
|
||||||
|
SrcIP: net.ParseIP("100.10.0.1"),
|
||||||
|
DstIP: net.ParseIP("100.10.0.100"),
|
||||||
|
Protocol: layers.IPProtocolUDP,
|
||||||
|
}
|
||||||
|
udp := &layers.UDP{
|
||||||
|
SrcPort: 51334,
|
||||||
|
DstPort: 53,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = udp.SetNetworkLayerForChecksum(ipv4)
|
||||||
|
require.NoError(t, err)
|
||||||
|
payload := gopacket.Payload("test")
|
||||||
|
|
||||||
|
buf := gopacket.NewSerializeBuffer()
|
||||||
|
opts := gopacket.SerializeOptions{
|
||||||
|
ComputeChecksums: true,
|
||||||
|
FixLengths: true,
|
||||||
|
}
|
||||||
|
err = gopacket.SerializeLayers(buf, opts, ipv4, udp, payload)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Test hook gets called
|
||||||
|
result := manager.processOutgoingHooks(buf.Bytes())
|
||||||
|
require.True(t, result)
|
||||||
|
require.True(t, hookCalled)
|
||||||
|
|
||||||
|
// Test non-UDP packet is ignored
|
||||||
|
ipv4.Protocol = layers.IPProtocolTCP
|
||||||
|
buf = gopacket.NewSerializeBuffer()
|
||||||
|
err = gopacket.SerializeLayers(buf, opts, ipv4)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
result = manager.processOutgoingHooks(buf.Bytes())
|
||||||
|
require.False(t, result)
|
||||||
|
}
|
||||||
|
|
||||||
func TestUSPFilterCreatePerformance(t *testing.T) {
|
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} {
|
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) {
|
t.Run(fmt.Sprintf("Testing %d rules", testMax), func(t *testing.T) {
|
||||||
@ -418,3 +505,213 @@ func TestUSPFilterCreatePerformance(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStatefulFirewall_UDPTracking(t *testing.T) {
|
||||||
|
manager, err := Create(&IFaceMock{
|
||||||
|
SetFilterFunc: func(device.PacketFilter) error { return nil },
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
manager.wgNetwork = &net.IPNet{
|
||||||
|
IP: net.ParseIP("100.10.0.0"),
|
||||||
|
Mask: net.CIDRMask(16, 32),
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.udpTracker.Close() // Close the existing tracker
|
||||||
|
manager.udpTracker = conntrack.NewUDPTracker(200 * time.Millisecond)
|
||||||
|
manager.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
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, manager.Reset(nil))
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Set up packet parameters
|
||||||
|
srcIP := net.ParseIP("100.10.0.1")
|
||||||
|
dstIP := net.ParseIP("100.10.0.100")
|
||||||
|
srcPort := uint16(51334)
|
||||||
|
dstPort := uint16(53)
|
||||||
|
|
||||||
|
// Create outbound packet
|
||||||
|
outboundIPv4 := &layers.IPv4{
|
||||||
|
TTL: 64,
|
||||||
|
Version: 4,
|
||||||
|
SrcIP: srcIP,
|
||||||
|
DstIP: dstIP,
|
||||||
|
Protocol: layers.IPProtocolUDP,
|
||||||
|
}
|
||||||
|
outboundUDP := &layers.UDP{
|
||||||
|
SrcPort: layers.UDPPort(srcPort),
|
||||||
|
DstPort: layers.UDPPort(dstPort),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = outboundUDP.SetNetworkLayerForChecksum(outboundIPv4)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
outboundBuf := gopacket.NewSerializeBuffer()
|
||||||
|
opts := gopacket.SerializeOptions{
|
||||||
|
ComputeChecksums: true,
|
||||||
|
FixLengths: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = gopacket.SerializeLayers(outboundBuf, opts,
|
||||||
|
outboundIPv4,
|
||||||
|
outboundUDP,
|
||||||
|
gopacket.Payload("test"),
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Process outbound packet and verify connection tracking
|
||||||
|
drop := manager.DropOutgoing(outboundBuf.Bytes())
|
||||||
|
require.False(t, drop, "Initial outbound packet should not be dropped")
|
||||||
|
|
||||||
|
// Verify connection was tracked
|
||||||
|
conn, exists := manager.udpTracker.GetConnection(srcIP, srcPort, dstIP, dstPort)
|
||||||
|
|
||||||
|
require.True(t, exists, "Connection should be tracked after outbound packet")
|
||||||
|
require.True(t, conntrack.ValidateIPs(conntrack.MakeIPAddr(srcIP), conn.SourceIP), "Source IP should match")
|
||||||
|
require.True(t, conntrack.ValidateIPs(conntrack.MakeIPAddr(dstIP), conn.DestIP), "Destination IP should match")
|
||||||
|
require.Equal(t, srcPort, conn.SourcePort, "Source port should match")
|
||||||
|
require.Equal(t, dstPort, conn.DestPort, "Destination port should match")
|
||||||
|
|
||||||
|
// Create valid inbound response packet
|
||||||
|
inboundIPv4 := &layers.IPv4{
|
||||||
|
TTL: 64,
|
||||||
|
Version: 4,
|
||||||
|
SrcIP: dstIP, // Original destination is now source
|
||||||
|
DstIP: srcIP, // Original source is now destination
|
||||||
|
Protocol: layers.IPProtocolUDP,
|
||||||
|
}
|
||||||
|
inboundUDP := &layers.UDP{
|
||||||
|
SrcPort: layers.UDPPort(dstPort), // Original destination port is now source
|
||||||
|
DstPort: layers.UDPPort(srcPort), // Original source port is now destination
|
||||||
|
}
|
||||||
|
|
||||||
|
err = inboundUDP.SetNetworkLayerForChecksum(inboundIPv4)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
inboundBuf := gopacket.NewSerializeBuffer()
|
||||||
|
err = gopacket.SerializeLayers(inboundBuf, opts,
|
||||||
|
inboundIPv4,
|
||||||
|
inboundUDP,
|
||||||
|
gopacket.Payload("response"),
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
// Test roundtrip response handling over time
|
||||||
|
checkPoints := []struct {
|
||||||
|
sleep time.Duration
|
||||||
|
shouldAllow bool
|
||||||
|
description string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
sleep: 0,
|
||||||
|
shouldAllow: true,
|
||||||
|
description: "Immediate response should be allowed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sleep: 50 * time.Millisecond,
|
||||||
|
shouldAllow: true,
|
||||||
|
description: "Response within timeout should be allowed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sleep: 100 * time.Millisecond,
|
||||||
|
shouldAllow: true,
|
||||||
|
description: "Response at half timeout should be allowed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// tracker hasn't updated conn for 250ms -> greater than 200ms timeout
|
||||||
|
sleep: 250 * time.Millisecond,
|
||||||
|
shouldAllow: false,
|
||||||
|
description: "Response after timeout should be dropped",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cp := range checkPoints {
|
||||||
|
time.Sleep(cp.sleep)
|
||||||
|
|
||||||
|
drop = manager.dropFilter(inboundBuf.Bytes(), manager.incomingRules)
|
||||||
|
require.Equal(t, cp.shouldAllow, !drop, cp.description)
|
||||||
|
|
||||||
|
// If the connection should still be valid, verify it exists
|
||||||
|
if cp.shouldAllow {
|
||||||
|
conn, exists := manager.udpTracker.GetConnection(srcIP, srcPort, dstIP, dstPort)
|
||||||
|
require.True(t, exists, "Connection should still exist during valid window")
|
||||||
|
require.True(t, time.Since(conn.GetLastSeen()) < manager.udpTracker.Timeout(),
|
||||||
|
"LastSeen should be updated for valid responses")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test invalid response packets (while connection is expired)
|
||||||
|
invalidCases := []struct {
|
||||||
|
name string
|
||||||
|
modifyFunc func(*layers.IPv4, *layers.UDP)
|
||||||
|
description string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "wrong source IP",
|
||||||
|
modifyFunc: func(ip *layers.IPv4, udp *layers.UDP) {
|
||||||
|
ip.SrcIP = net.ParseIP("100.10.0.101")
|
||||||
|
},
|
||||||
|
description: "Response from wrong IP should be dropped",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong destination IP",
|
||||||
|
modifyFunc: func(ip *layers.IPv4, udp *layers.UDP) {
|
||||||
|
ip.DstIP = net.ParseIP("100.10.0.2")
|
||||||
|
},
|
||||||
|
description: "Response to wrong IP should be dropped",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong source port",
|
||||||
|
modifyFunc: func(ip *layers.IPv4, udp *layers.UDP) {
|
||||||
|
udp.SrcPort = 54
|
||||||
|
},
|
||||||
|
description: "Response from wrong port should be dropped",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wrong destination port",
|
||||||
|
modifyFunc: func(ip *layers.IPv4, udp *layers.UDP) {
|
||||||
|
udp.DstPort = 51335
|
||||||
|
},
|
||||||
|
description: "Response to wrong port should be dropped",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new outbound connection for invalid tests
|
||||||
|
drop = manager.processOutgoingHooks(outboundBuf.Bytes())
|
||||||
|
require.False(t, drop, "Second outbound packet should not be dropped")
|
||||||
|
|
||||||
|
for _, tc := range invalidCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
testIPv4 := *inboundIPv4
|
||||||
|
testUDP := *inboundUDP
|
||||||
|
|
||||||
|
tc.modifyFunc(&testIPv4, &testUDP)
|
||||||
|
|
||||||
|
err = testUDP.SetNetworkLayerForChecksum(&testIPv4)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
testBuf := gopacket.NewSerializeBuffer()
|
||||||
|
err = gopacket.SerializeLayers(testBuf, opts,
|
||||||
|
&testIPv4,
|
||||||
|
&testUDP,
|
||||||
|
gopacket.Payload("response"),
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify the invalid packet is dropped
|
||||||
|
drop = manager.dropFilter(testBuf.Bytes(), manager.incomingRules)
|
||||||
|
require.True(t, drop, tc.description)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
226
client/internal/dns/handler_chain.go
Normal file
226
client/internal/dns/handler_chain.go
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PriorityDNSRoute = 100
|
||||||
|
PriorityMatchDomain = 50
|
||||||
|
PriorityDefault = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
type SubdomainMatcher interface {
|
||||||
|
dns.Handler
|
||||||
|
MatchSubdomains() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandlerEntry struct {
|
||||||
|
Handler dns.Handler
|
||||||
|
Priority int
|
||||||
|
Pattern string
|
||||||
|
OrigPattern string
|
||||||
|
IsWildcard bool
|
||||||
|
StopHandler handlerWithStop
|
||||||
|
MatchSubdomains bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerChain represents a prioritized chain of DNS handlers
|
||||||
|
type HandlerChain struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
handlers []HandlerEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseWriterChain wraps a dns.ResponseWriter to track if handler wants to continue chain
|
||||||
|
type ResponseWriterChain struct {
|
||||||
|
dns.ResponseWriter
|
||||||
|
origPattern string
|
||||||
|
shouldContinue bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *ResponseWriterChain) WriteMsg(m *dns.Msg) error {
|
||||||
|
// Check if this is a continue signal (NXDOMAIN with Zero bit set)
|
||||||
|
if m.Rcode == dns.RcodeNameError && m.MsgHdr.Zero {
|
||||||
|
w.shouldContinue = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return w.ResponseWriter.WriteMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandlerChain() *HandlerChain {
|
||||||
|
return &HandlerChain{
|
||||||
|
handlers: make([]HandlerEntry, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrigPattern returns the original pattern of the handler that wrote the response
|
||||||
|
func (w *ResponseWriterChain) GetOrigPattern() string {
|
||||||
|
return w.origPattern
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHandler adds a new handler to the chain, replacing any existing handler with the same pattern and priority
|
||||||
|
func (c *HandlerChain) AddHandler(pattern string, handler dns.Handler, priority int, stopHandler handlerWithStop) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
origPattern := pattern
|
||||||
|
isWildcard := strings.HasPrefix(pattern, "*.")
|
||||||
|
if isWildcard {
|
||||||
|
pattern = pattern[2:]
|
||||||
|
}
|
||||||
|
pattern = dns.Fqdn(pattern)
|
||||||
|
origPattern = dns.Fqdn(origPattern)
|
||||||
|
|
||||||
|
// First remove any existing handler with same original pattern and priority
|
||||||
|
for i := len(c.handlers) - 1; i >= 0; i-- {
|
||||||
|
if c.handlers[i].OrigPattern == origPattern && c.handlers[i].Priority == priority {
|
||||||
|
if c.handlers[i].StopHandler != nil {
|
||||||
|
c.handlers[i].StopHandler.stop()
|
||||||
|
}
|
||||||
|
c.handlers = append(c.handlers[:i], c.handlers[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if handler implements SubdomainMatcher interface
|
||||||
|
matchSubdomains := false
|
||||||
|
if matcher, ok := handler.(SubdomainMatcher); ok {
|
||||||
|
matchSubdomains = matcher.MatchSubdomains()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("adding handler pattern: domain=%s original: domain=%s wildcard=%v match_subdomain=%v priority=%d",
|
||||||
|
pattern, origPattern, isWildcard, matchSubdomains, priority)
|
||||||
|
|
||||||
|
entry := HandlerEntry{
|
||||||
|
Handler: handler,
|
||||||
|
Priority: priority,
|
||||||
|
Pattern: pattern,
|
||||||
|
OrigPattern: origPattern,
|
||||||
|
IsWildcard: isWildcard,
|
||||||
|
StopHandler: stopHandler,
|
||||||
|
MatchSubdomains: matchSubdomains,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert handler in priority order
|
||||||
|
pos := 0
|
||||||
|
for i, h := range c.handlers {
|
||||||
|
if h.Priority < priority {
|
||||||
|
pos = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pos = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
c.handlers = append(c.handlers[:pos], append([]HandlerEntry{entry}, c.handlers[pos:]...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveHandler removes a handler for the given pattern and priority
|
||||||
|
func (c *HandlerChain) RemoveHandler(pattern string, priority int) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
pattern = dns.Fqdn(pattern)
|
||||||
|
|
||||||
|
// Find and remove handlers matching both original pattern and priority
|
||||||
|
for i := len(c.handlers) - 1; i >= 0; i-- {
|
||||||
|
entry := c.handlers[i]
|
||||||
|
if entry.OrigPattern == pattern && entry.Priority == priority {
|
||||||
|
if entry.StopHandler != nil {
|
||||||
|
entry.StopHandler.stop()
|
||||||
|
}
|
||||||
|
c.handlers = append(c.handlers[:i], c.handlers[i+1:]...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasHandlers returns true if there are any handlers remaining for the given pattern
|
||||||
|
func (c *HandlerChain) HasHandlers(pattern string) bool {
|
||||||
|
c.mu.RLock()
|
||||||
|
defer c.mu.RUnlock()
|
||||||
|
|
||||||
|
pattern = dns.Fqdn(pattern)
|
||||||
|
for _, entry := range c.handlers {
|
||||||
|
if entry.Pattern == pattern {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HandlerChain) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||||
|
if len(r.Question) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
qname := r.Question[0].Name
|
||||||
|
log.Tracef("handling DNS request for domain=%s", qname)
|
||||||
|
|
||||||
|
c.mu.RLock()
|
||||||
|
handlers := slices.Clone(c.handlers)
|
||||||
|
c.mu.RUnlock()
|
||||||
|
|
||||||
|
if log.IsLevelEnabled(log.TraceLevel) {
|
||||||
|
log.Tracef("current handlers (%d):", len(handlers))
|
||||||
|
for _, h := range handlers {
|
||||||
|
log.Tracef(" - pattern: domain=%s original: domain=%s wildcard=%v priority=%d",
|
||||||
|
h.Pattern, h.OrigPattern, h.IsWildcard, h.Priority)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try handlers in priority order
|
||||||
|
for _, entry := range handlers {
|
||||||
|
var matched bool
|
||||||
|
switch {
|
||||||
|
case entry.Pattern == ".":
|
||||||
|
matched = true
|
||||||
|
case entry.IsWildcard:
|
||||||
|
parts := strings.Split(strings.TrimSuffix(qname, entry.Pattern), ".")
|
||||||
|
matched = len(parts) >= 2 && strings.HasSuffix(qname, entry.Pattern)
|
||||||
|
default:
|
||||||
|
// For non-wildcard patterns:
|
||||||
|
// If handler wants subdomain matching, allow suffix match
|
||||||
|
// Otherwise require exact match
|
||||||
|
if entry.MatchSubdomains {
|
||||||
|
matched = qname == entry.Pattern || strings.HasSuffix(qname, "."+entry.Pattern)
|
||||||
|
} else {
|
||||||
|
matched = qname == entry.Pattern
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matched {
|
||||||
|
log.Tracef("trying domain match: request: domain=%s pattern: domain=%s wildcard=%v match_subdomain=%v matched=false",
|
||||||
|
qname, entry.OrigPattern, entry.MatchSubdomains, entry.IsWildcard)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Tracef("handler matched: request: domain=%s pattern: domain=%s wildcard=%v match_subdomain=%v",
|
||||||
|
qname, entry.OrigPattern, entry.IsWildcard, entry.MatchSubdomains)
|
||||||
|
|
||||||
|
chainWriter := &ResponseWriterChain{
|
||||||
|
ResponseWriter: w,
|
||||||
|
origPattern: entry.OrigPattern,
|
||||||
|
}
|
||||||
|
entry.Handler.ServeDNS(chainWriter, r)
|
||||||
|
|
||||||
|
// If handler wants to continue, try next handler
|
||||||
|
if chainWriter.shouldContinue {
|
||||||
|
log.Tracef("handler requested continue to next handler")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// No handler matched or all handlers passed
|
||||||
|
log.Tracef("no handler found for domain=%s", qname)
|
||||||
|
resp := &dns.Msg{}
|
||||||
|
resp.SetRcode(r, dns.RcodeNameError)
|
||||||
|
if err := w.WriteMsg(resp); err != nil {
|
||||||
|
log.Errorf("failed to write DNS response: %v", err)
|
||||||
|
}
|
||||||
|
}
|
511
client/internal/dns/handler_chain_test.go
Normal file
511
client/internal/dns/handler_chain_test.go
Normal file
@ -0,0 +1,511 @@
|
|||||||
|
package dns_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
|
nbdns "github.com/netbirdio/netbird/client/internal/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestHandlerChain_ServeDNS_Priorities tests that handlers are executed in priority order
|
||||||
|
func TestHandlerChain_ServeDNS_Priorities(t *testing.T) {
|
||||||
|
chain := nbdns.NewHandlerChain()
|
||||||
|
|
||||||
|
// Create mock handlers for different priorities
|
||||||
|
defaultHandler := &nbdns.MockHandler{}
|
||||||
|
matchDomainHandler := &nbdns.MockHandler{}
|
||||||
|
dnsRouteHandler := &nbdns.MockHandler{}
|
||||||
|
|
||||||
|
// Setup handlers with different priorities
|
||||||
|
chain.AddHandler("example.com.", defaultHandler, nbdns.PriorityDefault, nil)
|
||||||
|
chain.AddHandler("example.com.", matchDomainHandler, nbdns.PriorityMatchDomain, nil)
|
||||||
|
chain.AddHandler("example.com.", dnsRouteHandler, nbdns.PriorityDNSRoute, nil)
|
||||||
|
|
||||||
|
// Create test request
|
||||||
|
r := new(dns.Msg)
|
||||||
|
r.SetQuestion("example.com.", dns.TypeA)
|
||||||
|
|
||||||
|
// Create test writer
|
||||||
|
w := &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}}
|
||||||
|
|
||||||
|
// Setup expectations - only highest priority handler should be called
|
||||||
|
dnsRouteHandler.On("ServeDNS", mock.Anything, r).Once()
|
||||||
|
matchDomainHandler.On("ServeDNS", mock.Anything, r).Maybe()
|
||||||
|
defaultHandler.On("ServeDNS", mock.Anything, r).Maybe()
|
||||||
|
|
||||||
|
// Execute
|
||||||
|
chain.ServeDNS(w, r)
|
||||||
|
|
||||||
|
// Verify all expectations were met
|
||||||
|
dnsRouteHandler.AssertExpectations(t)
|
||||||
|
matchDomainHandler.AssertExpectations(t)
|
||||||
|
defaultHandler.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHandlerChain_ServeDNS_DomainMatching tests various domain matching scenarios
|
||||||
|
func TestHandlerChain_ServeDNS_DomainMatching(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
handlerDomain string
|
||||||
|
queryDomain string
|
||||||
|
isWildcard bool
|
||||||
|
matchSubdomains bool
|
||||||
|
shouldMatch bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "exact match",
|
||||||
|
handlerDomain: "example.com.",
|
||||||
|
queryDomain: "example.com.",
|
||||||
|
isWildcard: false,
|
||||||
|
matchSubdomains: false,
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subdomain with non-wildcard and MatchSubdomains true",
|
||||||
|
handlerDomain: "example.com.",
|
||||||
|
queryDomain: "sub.example.com.",
|
||||||
|
isWildcard: false,
|
||||||
|
matchSubdomains: true,
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subdomain with non-wildcard and MatchSubdomains false",
|
||||||
|
handlerDomain: "example.com.",
|
||||||
|
queryDomain: "sub.example.com.",
|
||||||
|
isWildcard: false,
|
||||||
|
matchSubdomains: false,
|
||||||
|
shouldMatch: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wildcard match",
|
||||||
|
handlerDomain: "*.example.com.",
|
||||||
|
queryDomain: "sub.example.com.",
|
||||||
|
isWildcard: true,
|
||||||
|
matchSubdomains: false,
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wildcard no match on apex",
|
||||||
|
handlerDomain: "*.example.com.",
|
||||||
|
queryDomain: "example.com.",
|
||||||
|
isWildcard: true,
|
||||||
|
matchSubdomains: false,
|
||||||
|
shouldMatch: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "root zone match",
|
||||||
|
handlerDomain: ".",
|
||||||
|
queryDomain: "anything.com.",
|
||||||
|
isWildcard: false,
|
||||||
|
matchSubdomains: false,
|
||||||
|
shouldMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no match different domain",
|
||||||
|
handlerDomain: "example.com.",
|
||||||
|
queryDomain: "example.org.",
|
||||||
|
isWildcard: false,
|
||||||
|
matchSubdomains: false,
|
||||||
|
shouldMatch: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
chain := nbdns.NewHandlerChain()
|
||||||
|
var handler dns.Handler
|
||||||
|
|
||||||
|
if tt.matchSubdomains {
|
||||||
|
mockSubHandler := &nbdns.MockSubdomainHandler{Subdomains: true}
|
||||||
|
handler = mockSubHandler
|
||||||
|
if tt.shouldMatch {
|
||||||
|
mockSubHandler.On("ServeDNS", mock.Anything, mock.Anything).Once()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mockHandler := &nbdns.MockHandler{}
|
||||||
|
handler = mockHandler
|
||||||
|
if tt.shouldMatch {
|
||||||
|
mockHandler.On("ServeDNS", mock.Anything, mock.Anything).Once()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern := tt.handlerDomain
|
||||||
|
if tt.isWildcard {
|
||||||
|
pattern = "*." + tt.handlerDomain[2:]
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.AddHandler(pattern, handler, nbdns.PriorityDefault, nil)
|
||||||
|
|
||||||
|
r := new(dns.Msg)
|
||||||
|
r.SetQuestion(tt.queryDomain, dns.TypeA)
|
||||||
|
w := &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}}
|
||||||
|
|
||||||
|
chain.ServeDNS(w, r)
|
||||||
|
|
||||||
|
if h, ok := handler.(*nbdns.MockHandler); ok {
|
||||||
|
h.AssertExpectations(t)
|
||||||
|
} else if h, ok := handler.(*nbdns.MockSubdomainHandler); ok {
|
||||||
|
h.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHandlerChain_ServeDNS_OverlappingDomains tests behavior with overlapping domain patterns
|
||||||
|
func TestHandlerChain_ServeDNS_OverlappingDomains(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
handlers []struct {
|
||||||
|
pattern string
|
||||||
|
priority int
|
||||||
|
}
|
||||||
|
queryDomain string
|
||||||
|
expectedCalls int
|
||||||
|
expectedHandler int // index of the handler that should be called
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "wildcard and exact same priority - exact should win",
|
||||||
|
handlers: []struct {
|
||||||
|
pattern string
|
||||||
|
priority int
|
||||||
|
}{
|
||||||
|
{pattern: "*.example.com.", priority: nbdns.PriorityDefault},
|
||||||
|
{pattern: "example.com.", priority: nbdns.PriorityDefault},
|
||||||
|
},
|
||||||
|
queryDomain: "example.com.",
|
||||||
|
expectedCalls: 1,
|
||||||
|
expectedHandler: 1, // exact match handler should be called
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "higher priority wildcard over lower priority exact",
|
||||||
|
handlers: []struct {
|
||||||
|
pattern string
|
||||||
|
priority int
|
||||||
|
}{
|
||||||
|
{pattern: "example.com.", priority: nbdns.PriorityDefault},
|
||||||
|
{pattern: "*.example.com.", priority: nbdns.PriorityDNSRoute},
|
||||||
|
},
|
||||||
|
queryDomain: "test.example.com.",
|
||||||
|
expectedCalls: 1,
|
||||||
|
expectedHandler: 1, // higher priority wildcard handler should be called
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple wildcards different priorities",
|
||||||
|
handlers: []struct {
|
||||||
|
pattern string
|
||||||
|
priority int
|
||||||
|
}{
|
||||||
|
{pattern: "*.example.com.", priority: nbdns.PriorityDefault},
|
||||||
|
{pattern: "*.example.com.", priority: nbdns.PriorityMatchDomain},
|
||||||
|
{pattern: "*.example.com.", priority: nbdns.PriorityDNSRoute},
|
||||||
|
},
|
||||||
|
queryDomain: "test.example.com.",
|
||||||
|
expectedCalls: 1,
|
||||||
|
expectedHandler: 2, // highest priority handler should be called
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subdomain with mix of patterns",
|
||||||
|
handlers: []struct {
|
||||||
|
pattern string
|
||||||
|
priority int
|
||||||
|
}{
|
||||||
|
{pattern: "*.example.com.", priority: nbdns.PriorityDefault},
|
||||||
|
{pattern: "test.example.com.", priority: nbdns.PriorityMatchDomain},
|
||||||
|
{pattern: "*.test.example.com.", priority: nbdns.PriorityDNSRoute},
|
||||||
|
},
|
||||||
|
queryDomain: "sub.test.example.com.",
|
||||||
|
expectedCalls: 1,
|
||||||
|
expectedHandler: 2, // highest priority matching handler should be called
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "root zone with specific domain",
|
||||||
|
handlers: []struct {
|
||||||
|
pattern string
|
||||||
|
priority int
|
||||||
|
}{
|
||||||
|
{pattern: ".", priority: nbdns.PriorityDefault},
|
||||||
|
{pattern: "example.com.", priority: nbdns.PriorityDNSRoute},
|
||||||
|
},
|
||||||
|
queryDomain: "example.com.",
|
||||||
|
expectedCalls: 1,
|
||||||
|
expectedHandler: 1, // higher priority specific domain should win over root
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
chain := nbdns.NewHandlerChain()
|
||||||
|
var handlers []*nbdns.MockHandler
|
||||||
|
|
||||||
|
// Setup handlers and expectations
|
||||||
|
for i := range tt.handlers {
|
||||||
|
handler := &nbdns.MockHandler{}
|
||||||
|
handlers = append(handlers, handler)
|
||||||
|
|
||||||
|
// Set expectation based on whether this handler should be called
|
||||||
|
if i == tt.expectedHandler {
|
||||||
|
handler.On("ServeDNS", mock.Anything, mock.Anything).Once()
|
||||||
|
} else {
|
||||||
|
handler.On("ServeDNS", mock.Anything, mock.Anything).Maybe()
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.AddHandler(tt.handlers[i].pattern, handler, tt.handlers[i].priority, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and execute request
|
||||||
|
r := new(dns.Msg)
|
||||||
|
r.SetQuestion(tt.queryDomain, dns.TypeA)
|
||||||
|
w := &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}}
|
||||||
|
chain.ServeDNS(w, r)
|
||||||
|
|
||||||
|
// Verify expectations
|
||||||
|
for _, handler := range handlers {
|
||||||
|
handler.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHandlerChain_ServeDNS_ChainContinuation tests the chain continuation functionality
|
||||||
|
func TestHandlerChain_ServeDNS_ChainContinuation(t *testing.T) {
|
||||||
|
chain := nbdns.NewHandlerChain()
|
||||||
|
|
||||||
|
// Create handlers
|
||||||
|
handler1 := &nbdns.MockHandler{}
|
||||||
|
handler2 := &nbdns.MockHandler{}
|
||||||
|
handler3 := &nbdns.MockHandler{}
|
||||||
|
|
||||||
|
// Add handlers in priority order
|
||||||
|
chain.AddHandler("example.com.", handler1, nbdns.PriorityDNSRoute, nil)
|
||||||
|
chain.AddHandler("example.com.", handler2, nbdns.PriorityMatchDomain, nil)
|
||||||
|
chain.AddHandler("example.com.", handler3, nbdns.PriorityDefault, nil)
|
||||||
|
|
||||||
|
// Create test request
|
||||||
|
r := new(dns.Msg)
|
||||||
|
r.SetQuestion("example.com.", dns.TypeA)
|
||||||
|
|
||||||
|
// Setup mock responses to simulate chain continuation
|
||||||
|
handler1.On("ServeDNS", mock.Anything, r).Run(func(args mock.Arguments) {
|
||||||
|
// First handler signals continue
|
||||||
|
w := args.Get(0).(*nbdns.ResponseWriterChain)
|
||||||
|
resp := new(dns.Msg)
|
||||||
|
resp.SetRcode(r, dns.RcodeNameError)
|
||||||
|
resp.MsgHdr.Zero = true // Signal to continue
|
||||||
|
assert.NoError(t, w.WriteMsg(resp))
|
||||||
|
}).Once()
|
||||||
|
|
||||||
|
handler2.On("ServeDNS", mock.Anything, r).Run(func(args mock.Arguments) {
|
||||||
|
// Second handler signals continue
|
||||||
|
w := args.Get(0).(*nbdns.ResponseWriterChain)
|
||||||
|
resp := new(dns.Msg)
|
||||||
|
resp.SetRcode(r, dns.RcodeNameError)
|
||||||
|
resp.MsgHdr.Zero = true
|
||||||
|
assert.NoError(t, w.WriteMsg(resp))
|
||||||
|
}).Once()
|
||||||
|
|
||||||
|
handler3.On("ServeDNS", mock.Anything, r).Run(func(args mock.Arguments) {
|
||||||
|
// Last handler responds normally
|
||||||
|
w := args.Get(0).(*nbdns.ResponseWriterChain)
|
||||||
|
resp := new(dns.Msg)
|
||||||
|
resp.SetRcode(r, dns.RcodeSuccess)
|
||||||
|
assert.NoError(t, w.WriteMsg(resp))
|
||||||
|
}).Once()
|
||||||
|
|
||||||
|
// Execute
|
||||||
|
w := &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}}
|
||||||
|
chain.ServeDNS(w, r)
|
||||||
|
|
||||||
|
// Verify all handlers were called in order
|
||||||
|
handler1.AssertExpectations(t)
|
||||||
|
handler2.AssertExpectations(t)
|
||||||
|
handler3.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mockResponseWriter implements dns.ResponseWriter for testing
|
||||||
|
type mockResponseWriter struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockResponseWriter) LocalAddr() net.Addr { return nil }
|
||||||
|
func (m *mockResponseWriter) RemoteAddr() net.Addr { return nil }
|
||||||
|
func (m *mockResponseWriter) WriteMsg(*dns.Msg) error { return nil }
|
||||||
|
func (m *mockResponseWriter) Write([]byte) (int, error) { return 0, nil }
|
||||||
|
func (m *mockResponseWriter) Close() error { return nil }
|
||||||
|
func (m *mockResponseWriter) TsigStatus() error { return nil }
|
||||||
|
func (m *mockResponseWriter) TsigTimersOnly(bool) {}
|
||||||
|
func (m *mockResponseWriter) Hijack() {}
|
||||||
|
|
||||||
|
func TestHandlerChain_PriorityDeregistration(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
ops []struct {
|
||||||
|
action string // "add" or "remove"
|
||||||
|
pattern string
|
||||||
|
priority int
|
||||||
|
}
|
||||||
|
query string
|
||||||
|
expectedCalls map[int]bool // map[priority]shouldBeCalled
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "remove high priority keeps lower priority handler",
|
||||||
|
ops: []struct {
|
||||||
|
action string
|
||||||
|
pattern string
|
||||||
|
priority int
|
||||||
|
}{
|
||||||
|
{"add", "example.com.", nbdns.PriorityDNSRoute},
|
||||||
|
{"add", "example.com.", nbdns.PriorityMatchDomain},
|
||||||
|
{"remove", "example.com.", nbdns.PriorityDNSRoute},
|
||||||
|
},
|
||||||
|
query: "example.com.",
|
||||||
|
expectedCalls: map[int]bool{
|
||||||
|
nbdns.PriorityDNSRoute: false,
|
||||||
|
nbdns.PriorityMatchDomain: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove lower priority keeps high priority handler",
|
||||||
|
ops: []struct {
|
||||||
|
action string
|
||||||
|
pattern string
|
||||||
|
priority int
|
||||||
|
}{
|
||||||
|
{"add", "example.com.", nbdns.PriorityDNSRoute},
|
||||||
|
{"add", "example.com.", nbdns.PriorityMatchDomain},
|
||||||
|
{"remove", "example.com.", nbdns.PriorityMatchDomain},
|
||||||
|
},
|
||||||
|
query: "example.com.",
|
||||||
|
expectedCalls: map[int]bool{
|
||||||
|
nbdns.PriorityDNSRoute: true,
|
||||||
|
nbdns.PriorityMatchDomain: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "remove all handlers in order",
|
||||||
|
ops: []struct {
|
||||||
|
action string
|
||||||
|
pattern string
|
||||||
|
priority int
|
||||||
|
}{
|
||||||
|
{"add", "example.com.", nbdns.PriorityDNSRoute},
|
||||||
|
{"add", "example.com.", nbdns.PriorityMatchDomain},
|
||||||
|
{"add", "example.com.", nbdns.PriorityDefault},
|
||||||
|
{"remove", "example.com.", nbdns.PriorityDNSRoute},
|
||||||
|
{"remove", "example.com.", nbdns.PriorityMatchDomain},
|
||||||
|
},
|
||||||
|
query: "example.com.",
|
||||||
|
expectedCalls: map[int]bool{
|
||||||
|
nbdns.PriorityDNSRoute: false,
|
||||||
|
nbdns.PriorityMatchDomain: false,
|
||||||
|
nbdns.PriorityDefault: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
chain := nbdns.NewHandlerChain()
|
||||||
|
handlers := make(map[int]*nbdns.MockHandler)
|
||||||
|
|
||||||
|
// Execute operations
|
||||||
|
for _, op := range tt.ops {
|
||||||
|
if op.action == "add" {
|
||||||
|
handler := &nbdns.MockHandler{}
|
||||||
|
handlers[op.priority] = handler
|
||||||
|
chain.AddHandler(op.pattern, handler, op.priority, nil)
|
||||||
|
} else {
|
||||||
|
chain.RemoveHandler(op.pattern, op.priority)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create test request
|
||||||
|
r := new(dns.Msg)
|
||||||
|
r.SetQuestion(tt.query, dns.TypeA)
|
||||||
|
w := &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}}
|
||||||
|
|
||||||
|
// Setup expectations
|
||||||
|
for priority, handler := range handlers {
|
||||||
|
if shouldCall, exists := tt.expectedCalls[priority]; exists && shouldCall {
|
||||||
|
handler.On("ServeDNS", mock.Anything, r).Once()
|
||||||
|
} else {
|
||||||
|
handler.On("ServeDNS", mock.Anything, r).Maybe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute request
|
||||||
|
chain.ServeDNS(w, r)
|
||||||
|
|
||||||
|
// Verify expectations
|
||||||
|
for _, handler := range handlers {
|
||||||
|
handler.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify handler exists check
|
||||||
|
for priority, shouldExist := range tt.expectedCalls {
|
||||||
|
if shouldExist {
|
||||||
|
assert.True(t, chain.HasHandlers(tt.ops[0].pattern),
|
||||||
|
"Handler chain should have handlers for pattern after removing priority %d", priority)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandlerChain_MultiPriorityHandling(t *testing.T) {
|
||||||
|
chain := nbdns.NewHandlerChain()
|
||||||
|
|
||||||
|
testDomain := "example.com."
|
||||||
|
testQuery := "test.example.com."
|
||||||
|
|
||||||
|
// Create handlers with MatchSubdomains enabled
|
||||||
|
routeHandler := &nbdns.MockSubdomainHandler{Subdomains: true}
|
||||||
|
matchHandler := &nbdns.MockSubdomainHandler{Subdomains: true}
|
||||||
|
defaultHandler := &nbdns.MockSubdomainHandler{Subdomains: true}
|
||||||
|
|
||||||
|
// Create test request that will be reused
|
||||||
|
r := new(dns.Msg)
|
||||||
|
r.SetQuestion(testQuery, dns.TypeA)
|
||||||
|
|
||||||
|
// Add handlers in mixed order
|
||||||
|
chain.AddHandler(testDomain, defaultHandler, nbdns.PriorityDefault, nil)
|
||||||
|
chain.AddHandler(testDomain, routeHandler, nbdns.PriorityDNSRoute, nil)
|
||||||
|
chain.AddHandler(testDomain, matchHandler, nbdns.PriorityMatchDomain, nil)
|
||||||
|
|
||||||
|
// Test 1: Initial state with all three handlers
|
||||||
|
w := &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}}
|
||||||
|
// Highest priority handler (routeHandler) should be called
|
||||||
|
routeHandler.On("ServeDNS", mock.Anything, r).Return().Once()
|
||||||
|
|
||||||
|
chain.ServeDNS(w, r)
|
||||||
|
routeHandler.AssertExpectations(t)
|
||||||
|
|
||||||
|
// Test 2: Remove highest priority handler
|
||||||
|
chain.RemoveHandler(testDomain, nbdns.PriorityDNSRoute)
|
||||||
|
assert.True(t, chain.HasHandlers(testDomain))
|
||||||
|
|
||||||
|
w = &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}}
|
||||||
|
// Now middle priority handler (matchHandler) should be called
|
||||||
|
matchHandler.On("ServeDNS", mock.Anything, r).Return().Once()
|
||||||
|
|
||||||
|
chain.ServeDNS(w, r)
|
||||||
|
matchHandler.AssertExpectations(t)
|
||||||
|
|
||||||
|
// Test 3: Remove middle priority handler
|
||||||
|
chain.RemoveHandler(testDomain, nbdns.PriorityMatchDomain)
|
||||||
|
assert.True(t, chain.HasHandlers(testDomain))
|
||||||
|
|
||||||
|
w = &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}}
|
||||||
|
// Now lowest priority handler (defaultHandler) should be called
|
||||||
|
defaultHandler.On("ServeDNS", mock.Anything, r).Return().Once()
|
||||||
|
|
||||||
|
chain.ServeDNS(w, r)
|
||||||
|
defaultHandler.AssertExpectations(t)
|
||||||
|
|
||||||
|
// Test 4: Remove last handler
|
||||||
|
chain.RemoveHandler(testDomain, nbdns.PriorityDefault)
|
||||||
|
assert.False(t, chain.HasHandlers(testDomain))
|
||||||
|
}
|
@ -17,12 +17,24 @@ type localResolver struct {
|
|||||||
records sync.Map
|
records sync.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *localResolver) MatchSubdomains() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (d *localResolver) stop() {
|
func (d *localResolver) stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the local resolver
|
||||||
|
func (d *localResolver) String() string {
|
||||||
|
return fmt.Sprintf("local resolver [%d records]", len(d.registeredMap))
|
||||||
|
}
|
||||||
|
|
||||||
// ServeDNS handles a DNS request
|
// ServeDNS handles a DNS request
|
||||||
func (d *localResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
func (d *localResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||||
log.Tracef("received question: %#v", r.Question[0])
|
if len(r.Question) > 0 {
|
||||||
|
log.Tracef("received question: domain=%s type=%v class=%v", r.Question[0].Name, r.Question[0].Qtype, r.Question[0].Qclass)
|
||||||
|
}
|
||||||
|
|
||||||
replyMessage := &dns.Msg{}
|
replyMessage := &dns.Msg{}
|
||||||
replyMessage.SetReply(r)
|
replyMessage.SetReply(r)
|
||||||
replyMessage.RecursionAvailable = true
|
replyMessage.RecursionAvailable = true
|
||||||
|
@ -3,14 +3,30 @@ package dns
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockServer is the mock instance of a dns server
|
// MockServer is the mock instance of a dns server
|
||||||
type MockServer struct {
|
type MockServer struct {
|
||||||
InitializeFunc func() error
|
InitializeFunc func() error
|
||||||
StopFunc func()
|
StopFunc func()
|
||||||
UpdateDNSServerFunc func(serial uint64, update nbdns.Config) error
|
UpdateDNSServerFunc func(serial uint64, update nbdns.Config) error
|
||||||
|
RegisterHandlerFunc func([]string, dns.Handler, int)
|
||||||
|
DeregisterHandlerFunc func([]string, int)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockServer) RegisterHandler(domains []string, handler dns.Handler, priority int) {
|
||||||
|
if m.RegisterHandlerFunc != nil {
|
||||||
|
m.RegisterHandlerFunc(domains, handler, priority)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockServer) DeregisterHandler(domains []string, priority int) {
|
||||||
|
if m.DeregisterHandlerFunc != nil {
|
||||||
|
m.DeregisterHandlerFunc(domains, priority)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize mock implementation of Initialize from Server interface
|
// Initialize mock implementation of Initialize from Server interface
|
||||||
|
@ -30,6 +30,8 @@ type IosDnsManager interface {
|
|||||||
|
|
||||||
// Server is a dns server interface
|
// Server is a dns server interface
|
||||||
type Server interface {
|
type Server interface {
|
||||||
|
RegisterHandler(domains []string, handler dns.Handler, priority int)
|
||||||
|
DeregisterHandler(domains []string, priority int)
|
||||||
Initialize() error
|
Initialize() error
|
||||||
Stop()
|
Stop()
|
||||||
DnsIP() string
|
DnsIP() string
|
||||||
@ -48,12 +50,14 @@ type DefaultServer struct {
|
|||||||
mux sync.Mutex
|
mux sync.Mutex
|
||||||
service service
|
service service
|
||||||
dnsMuxMap registeredHandlerMap
|
dnsMuxMap registeredHandlerMap
|
||||||
|
handlerPriorities map[string]int
|
||||||
localResolver *localResolver
|
localResolver *localResolver
|
||||||
wgInterface WGIface
|
wgInterface WGIface
|
||||||
hostManager hostManager
|
hostManager hostManager
|
||||||
updateSerial uint64
|
updateSerial uint64
|
||||||
previousConfigHash uint64
|
previousConfigHash uint64
|
||||||
currentConfig HostDNSConfig
|
currentConfig HostDNSConfig
|
||||||
|
handlerChain *HandlerChain
|
||||||
|
|
||||||
// permanent related properties
|
// permanent related properties
|
||||||
permanent bool
|
permanent bool
|
||||||
@ -74,8 +78,9 @@ type handlerWithStop interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type muxUpdate struct {
|
type muxUpdate struct {
|
||||||
domain string
|
domain string
|
||||||
handler handlerWithStop
|
handler handlerWithStop
|
||||||
|
priority int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDefaultServer returns a new dns server
|
// NewDefaultServer returns a new dns server
|
||||||
@ -135,10 +140,12 @@ func NewDefaultServerIos(
|
|||||||
func newDefaultServer(ctx context.Context, wgInterface WGIface, dnsService service, statusRecorder *peer.Status, stateManager *statemanager.Manager) *DefaultServer {
|
func newDefaultServer(ctx context.Context, wgInterface WGIface, dnsService service, statusRecorder *peer.Status, stateManager *statemanager.Manager) *DefaultServer {
|
||||||
ctx, stop := context.WithCancel(ctx)
|
ctx, stop := context.WithCancel(ctx)
|
||||||
defaultServer := &DefaultServer{
|
defaultServer := &DefaultServer{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
ctxCancel: stop,
|
ctxCancel: stop,
|
||||||
service: dnsService,
|
service: dnsService,
|
||||||
dnsMuxMap: make(registeredHandlerMap),
|
handlerChain: NewHandlerChain(),
|
||||||
|
dnsMuxMap: make(registeredHandlerMap),
|
||||||
|
handlerPriorities: make(map[string]int),
|
||||||
localResolver: &localResolver{
|
localResolver: &localResolver{
|
||||||
registeredMap: make(registrationMap),
|
registeredMap: make(registrationMap),
|
||||||
},
|
},
|
||||||
@ -151,6 +158,51 @@ func newDefaultServer(ctx context.Context, wgInterface WGIface, dnsService servi
|
|||||||
return defaultServer
|
return defaultServer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DefaultServer) RegisterHandler(domains []string, handler dns.Handler, priority int) {
|
||||||
|
s.mux.Lock()
|
||||||
|
defer s.mux.Unlock()
|
||||||
|
|
||||||
|
s.registerHandler(domains, handler, priority)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultServer) registerHandler(domains []string, handler dns.Handler, priority int) {
|
||||||
|
log.Debugf("registering handler %s with priority %d", handler, priority)
|
||||||
|
|
||||||
|
for _, domain := range domains {
|
||||||
|
if domain == "" {
|
||||||
|
log.Warn("skipping empty domain")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.handlerChain.AddHandler(domain, handler, priority, nil)
|
||||||
|
s.handlerPriorities[domain] = priority
|
||||||
|
s.service.RegisterMux(nbdns.NormalizeZone(domain), s.handlerChain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultServer) DeregisterHandler(domains []string, priority int) {
|
||||||
|
s.mux.Lock()
|
||||||
|
defer s.mux.Unlock()
|
||||||
|
|
||||||
|
s.deregisterHandler(domains, priority)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultServer) deregisterHandler(domains []string, priority int) {
|
||||||
|
log.Debugf("deregistering handler %v with priority %d", domains, priority)
|
||||||
|
|
||||||
|
for _, domain := range domains {
|
||||||
|
s.handlerChain.RemoveHandler(domain, priority)
|
||||||
|
|
||||||
|
// Only deregister from service if no handlers remain
|
||||||
|
if !s.handlerChain.HasHandlers(domain) {
|
||||||
|
if domain == "" {
|
||||||
|
log.Warn("skipping empty domain")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.service.DeregisterMux(nbdns.NormalizeZone(domain))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize instantiate host manager and the dns service
|
// Initialize instantiate host manager and the dns service
|
||||||
func (s *DefaultServer) Initialize() (err error) {
|
func (s *DefaultServer) Initialize() (err error) {
|
||||||
s.mux.Lock()
|
s.mux.Lock()
|
||||||
@ -343,14 +395,14 @@ func (s *DefaultServer) buildLocalHandlerUpdate(customZones []nbdns.CustomZone)
|
|||||||
localRecords := make(map[string]nbdns.SimpleRecord, 0)
|
localRecords := make(map[string]nbdns.SimpleRecord, 0)
|
||||||
|
|
||||||
for _, customZone := range customZones {
|
for _, customZone := range customZones {
|
||||||
|
|
||||||
if len(customZone.Records) == 0 {
|
if len(customZone.Records) == 0 {
|
||||||
return nil, nil, fmt.Errorf("received an empty list of records")
|
return nil, nil, fmt.Errorf("received an empty list of records")
|
||||||
}
|
}
|
||||||
|
|
||||||
muxUpdates = append(muxUpdates, muxUpdate{
|
muxUpdates = append(muxUpdates, muxUpdate{
|
||||||
domain: customZone.Domain,
|
domain: customZone.Domain,
|
||||||
handler: s.localResolver,
|
handler: s.localResolver,
|
||||||
|
priority: PriorityMatchDomain,
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, record := range customZone.Records {
|
for _, record := range customZone.Records {
|
||||||
@ -412,8 +464,9 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
|
|||||||
|
|
||||||
if nsGroup.Primary {
|
if nsGroup.Primary {
|
||||||
muxUpdates = append(muxUpdates, muxUpdate{
|
muxUpdates = append(muxUpdates, muxUpdate{
|
||||||
domain: nbdns.RootZone,
|
domain: nbdns.RootZone,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
|
priority: PriorityDefault,
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -429,8 +482,9 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
|
|||||||
return nil, fmt.Errorf("received a nameserver group with an empty domain element")
|
return nil, fmt.Errorf("received a nameserver group with an empty domain element")
|
||||||
}
|
}
|
||||||
muxUpdates = append(muxUpdates, muxUpdate{
|
muxUpdates = append(muxUpdates, muxUpdate{
|
||||||
domain: domain,
|
domain: domain,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
|
priority: PriorityMatchDomain,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -440,12 +494,16 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
|
|||||||
|
|
||||||
func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) {
|
func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) {
|
||||||
muxUpdateMap := make(registeredHandlerMap)
|
muxUpdateMap := make(registeredHandlerMap)
|
||||||
|
handlersByPriority := make(map[string]int)
|
||||||
|
|
||||||
var isContainRootUpdate bool
|
var isContainRootUpdate bool
|
||||||
|
|
||||||
|
// First register new handlers
|
||||||
for _, update := range muxUpdates {
|
for _, update := range muxUpdates {
|
||||||
s.service.RegisterMux(update.domain, update.handler)
|
s.registerHandler([]string{update.domain}, update.handler, update.priority)
|
||||||
muxUpdateMap[update.domain] = update.handler
|
muxUpdateMap[update.domain] = update.handler
|
||||||
|
handlersByPriority[update.domain] = update.priority
|
||||||
|
|
||||||
if existingHandler, ok := s.dnsMuxMap[update.domain]; ok {
|
if existingHandler, ok := s.dnsMuxMap[update.domain]; ok {
|
||||||
existingHandler.stop()
|
existingHandler.stop()
|
||||||
}
|
}
|
||||||
@ -455,6 +513,7 @@ func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Then deregister old handlers not in the update
|
||||||
for key, existingHandler := range s.dnsMuxMap {
|
for key, existingHandler := range s.dnsMuxMap {
|
||||||
_, found := muxUpdateMap[key]
|
_, found := muxUpdateMap[key]
|
||||||
if !found {
|
if !found {
|
||||||
@ -463,12 +522,16 @@ func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) {
|
|||||||
existingHandler.stop()
|
existingHandler.stop()
|
||||||
} else {
|
} else {
|
||||||
existingHandler.stop()
|
existingHandler.stop()
|
||||||
s.service.DeregisterMux(key)
|
// Deregister with the priority that was used to register
|
||||||
|
if oldPriority, ok := s.handlerPriorities[key]; ok {
|
||||||
|
s.deregisterHandler([]string{key}, oldPriority)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.dnsMuxMap = muxUpdateMap
|
s.dnsMuxMap = muxUpdateMap
|
||||||
|
s.handlerPriorities = handlersByPriority
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultServer) updateLocalResolver(update map[string]nbdns.SimpleRecord) {
|
func (s *DefaultServer) updateLocalResolver(update map[string]nbdns.SimpleRecord) {
|
||||||
@ -517,13 +580,13 @@ func (s *DefaultServer) upstreamCallbacks(
|
|||||||
if nsGroup.Primary {
|
if nsGroup.Primary {
|
||||||
removeIndex[nbdns.RootZone] = -1
|
removeIndex[nbdns.RootZone] = -1
|
||||||
s.currentConfig.RouteAll = false
|
s.currentConfig.RouteAll = false
|
||||||
s.service.DeregisterMux(nbdns.RootZone)
|
s.deregisterHandler([]string{nbdns.RootZone}, PriorityDefault)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, item := range s.currentConfig.Domains {
|
for i, item := range s.currentConfig.Domains {
|
||||||
if _, found := removeIndex[item.Domain]; found {
|
if _, found := removeIndex[item.Domain]; found {
|
||||||
s.currentConfig.Domains[i].Disabled = true
|
s.currentConfig.Domains[i].Disabled = true
|
||||||
s.service.DeregisterMux(item.Domain)
|
s.deregisterHandler([]string{item.Domain}, PriorityMatchDomain)
|
||||||
removeIndex[item.Domain] = i
|
removeIndex[item.Domain] = i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -554,7 +617,7 @@ func (s *DefaultServer) upstreamCallbacks(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
s.currentConfig.Domains[i].Disabled = false
|
s.currentConfig.Domains[i].Disabled = false
|
||||||
s.service.RegisterMux(domain, handler)
|
s.registerHandler([]string{domain}, handler, PriorityMatchDomain)
|
||||||
}
|
}
|
||||||
|
|
||||||
l := log.WithField("nameservers", nsGroup.NameServers)
|
l := log.WithField("nameservers", nsGroup.NameServers)
|
||||||
@ -562,7 +625,7 @@ func (s *DefaultServer) upstreamCallbacks(
|
|||||||
|
|
||||||
if nsGroup.Primary {
|
if nsGroup.Primary {
|
||||||
s.currentConfig.RouteAll = true
|
s.currentConfig.RouteAll = true
|
||||||
s.service.RegisterMux(nbdns.RootZone, handler)
|
s.registerHandler([]string{nbdns.RootZone}, handler, PriorityDefault)
|
||||||
}
|
}
|
||||||
if err := s.hostManager.applyDNSConfig(s.currentConfig, s.stateManager); err != nil {
|
if err := s.hostManager.applyDNSConfig(s.currentConfig, s.stateManager); err != nil {
|
||||||
l.WithError(err).Error("reactivate temporary disabled nameserver group, DNS update apply")
|
l.WithError(err).Error("reactivate temporary disabled nameserver group, DNS update apply")
|
||||||
@ -593,7 +656,8 @@ func (s *DefaultServer) addHostRootZone() {
|
|||||||
}
|
}
|
||||||
handler.deactivate = func(error) {}
|
handler.deactivate = func(error) {}
|
||||||
handler.reactivate = func() {}
|
handler.reactivate = func() {}
|
||||||
s.service.RegisterMux(nbdns.RootZone, handler)
|
|
||||||
|
s.registerHandler([]string{nbdns.RootZone}, handler, PriorityDefault)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultServer) updateNSGroupStates(groups []*nbdns.NameServerGroup) {
|
func (s *DefaultServer) updateNSGroupStates(groups []*nbdns.NameServerGroup) {
|
||||||
|
@ -11,7 +11,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/miekg/dns"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/firewall/uspfilter"
|
"github.com/netbirdio/netbird/client/firewall/uspfilter"
|
||||||
@ -512,7 +514,7 @@ func TestDNSServerStartStop(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsServer.service.RegisterMux("netbird.cloud", dnsServer.localResolver)
|
dnsServer.registerHandler([]string{"netbird.cloud"}, dnsServer.localResolver, 1)
|
||||||
|
|
||||||
resolver := &net.Resolver{
|
resolver := &net.Resolver{
|
||||||
PreferGo: true,
|
PreferGo: true,
|
||||||
@ -560,7 +562,9 @@ func TestDNSServerUpstreamDeactivateCallback(t *testing.T) {
|
|||||||
localResolver: &localResolver{
|
localResolver: &localResolver{
|
||||||
registeredMap: make(registrationMap),
|
registeredMap: make(registrationMap),
|
||||||
},
|
},
|
||||||
hostManager: hostManager,
|
handlerChain: NewHandlerChain(),
|
||||||
|
handlerPriorities: make(map[string]int),
|
||||||
|
hostManager: hostManager,
|
||||||
currentConfig: HostDNSConfig{
|
currentConfig: HostDNSConfig{
|
||||||
Domains: []DomainConfig{
|
Domains: []DomainConfig{
|
||||||
{false, "domain0", false},
|
{false, "domain0", false},
|
||||||
@ -872,3 +876,86 @@ func newDnsResolver(ip string, port int) *net.Resolver {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MockHandler implements dns.Handler interface for testing
|
||||||
|
type MockHandler struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||||
|
m.Called(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockSubdomainHandler struct {
|
||||||
|
MockHandler
|
||||||
|
Subdomains bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockSubdomainHandler) MatchSubdomains() bool {
|
||||||
|
return m.Subdomains
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandlerChain_DomainPriorities(t *testing.T) {
|
||||||
|
chain := NewHandlerChain()
|
||||||
|
|
||||||
|
dnsRouteHandler := &MockHandler{}
|
||||||
|
upstreamHandler := &MockSubdomainHandler{
|
||||||
|
Subdomains: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.AddHandler("example.com.", dnsRouteHandler, PriorityDNSRoute, nil)
|
||||||
|
chain.AddHandler("example.com.", upstreamHandler, PriorityMatchDomain, nil)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
query string
|
||||||
|
expectedHandler dns.Handler
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "exact domain with dns route handler",
|
||||||
|
query: "example.com.",
|
||||||
|
expectedHandler: dnsRouteHandler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subdomain should use upstream handler",
|
||||||
|
query: "sub.example.com.",
|
||||||
|
expectedHandler: upstreamHandler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "deep subdomain should use upstream handler",
|
||||||
|
query: "deep.sub.example.com.",
|
||||||
|
expectedHandler: upstreamHandler,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
r := new(dns.Msg)
|
||||||
|
r.SetQuestion(tc.query, dns.TypeA)
|
||||||
|
w := &ResponseWriterChain{ResponseWriter: &mockResponseWriter{}}
|
||||||
|
|
||||||
|
if mh, ok := tc.expectedHandler.(*MockHandler); ok {
|
||||||
|
mh.On("ServeDNS", mock.Anything, r).Once()
|
||||||
|
} else if mh, ok := tc.expectedHandler.(*MockSubdomainHandler); ok {
|
||||||
|
mh.On("ServeDNS", mock.Anything, r).Once()
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.ServeDNS(w, r)
|
||||||
|
|
||||||
|
if mh, ok := tc.expectedHandler.(*MockHandler); ok {
|
||||||
|
mh.AssertExpectations(t)
|
||||||
|
} else if mh, ok := tc.expectedHandler.(*MockSubdomainHandler); ok {
|
||||||
|
mh.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset mocks
|
||||||
|
if mh, ok := tc.expectedHandler.(*MockHandler); ok {
|
||||||
|
mh.ExpectedCalls = nil
|
||||||
|
mh.Calls = nil
|
||||||
|
} else if mh, ok := tc.expectedHandler.(*MockSubdomainHandler); ok {
|
||||||
|
mh.ExpectedCalls = nil
|
||||||
|
mh.Calls = nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -105,6 +105,7 @@ func (s *serviceViaListener) Stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceViaListener) RegisterMux(pattern string, handler dns.Handler) {
|
func (s *serviceViaListener) RegisterMux(pattern string, handler dns.Handler) {
|
||||||
|
log.Debugf("registering dns handler for pattern: %s", pattern)
|
||||||
s.dnsMux.Handle(pattern, handler)
|
s.dnsMux.Handle(pattern, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +66,15 @@ func newUpstreamResolverBase(ctx context.Context, statusRecorder *peer.Status) *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String returns a string representation of the upstream resolver
|
||||||
|
func (u *upstreamResolverBase) String() string {
|
||||||
|
return fmt.Sprintf("upstream %v", u.upstreamServers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *upstreamResolverBase) MatchSubdomains() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (u *upstreamResolverBase) stop() {
|
func (u *upstreamResolverBase) stop() {
|
||||||
log.Debugf("stopping serving DNS for upstreams %s", u.upstreamServers)
|
log.Debugf("stopping serving DNS for upstreams %s", u.upstreamServers)
|
||||||
u.cancel()
|
u.cancel()
|
||||||
|
157
client/internal/dnsfwd/forwarder.go
Normal file
157
client/internal/dnsfwd/forwarder.go
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
package dnsfwd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
const errResolveFailed = "failed to resolve query for domain=%s: %v"
|
||||||
|
|
||||||
|
type DNSForwarder struct {
|
||||||
|
listenAddress string
|
||||||
|
ttl uint32
|
||||||
|
domains []string
|
||||||
|
|
||||||
|
dnsServer *dns.Server
|
||||||
|
mux *dns.ServeMux
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDNSForwarder(listenAddress string, ttl uint32) *DNSForwarder {
|
||||||
|
log.Debugf("creating DNS forwarder with listen_address=%s ttl=%d", listenAddress, ttl)
|
||||||
|
return &DNSForwarder{
|
||||||
|
listenAddress: listenAddress,
|
||||||
|
ttl: ttl,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *DNSForwarder) Listen(domains []string) error {
|
||||||
|
log.Infof("listen DNS forwarder on address=%s", f.listenAddress)
|
||||||
|
mux := dns.NewServeMux()
|
||||||
|
|
||||||
|
dnsServer := &dns.Server{
|
||||||
|
Addr: f.listenAddress,
|
||||||
|
Net: "udp",
|
||||||
|
Handler: mux,
|
||||||
|
}
|
||||||
|
f.dnsServer = dnsServer
|
||||||
|
f.mux = mux
|
||||||
|
|
||||||
|
f.UpdateDomains(domains)
|
||||||
|
|
||||||
|
return dnsServer.ListenAndServe()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *DNSForwarder) UpdateDomains(domains []string) {
|
||||||
|
log.Debugf("Updating domains from %v to %v", f.domains, domains)
|
||||||
|
|
||||||
|
for _, d := range f.domains {
|
||||||
|
f.mux.HandleRemove(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
newDomains := filterDomains(domains)
|
||||||
|
for _, d := range newDomains {
|
||||||
|
f.mux.HandleFunc(d, f.handleDNSQuery)
|
||||||
|
}
|
||||||
|
f.domains = newDomains
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *DNSForwarder) Close(ctx context.Context) error {
|
||||||
|
if f.dnsServer == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return f.dnsServer.ShutdownContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *DNSForwarder) handleDNSQuery(w dns.ResponseWriter, query *dns.Msg) {
|
||||||
|
if len(query.Question) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Tracef("received DNS request for DNS forwarder: domain=%v type=%v class=%v",
|
||||||
|
query.Question[0].Name, query.Question[0].Qtype, query.Question[0].Qclass)
|
||||||
|
|
||||||
|
question := query.Question[0]
|
||||||
|
domain := question.Name
|
||||||
|
|
||||||
|
resp := query.SetReply(query)
|
||||||
|
|
||||||
|
ips, err := net.LookupIP(domain)
|
||||||
|
if err != nil {
|
||||||
|
var dnsErr *net.DNSError
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case errors.As(err, &dnsErr):
|
||||||
|
resp.Rcode = dns.RcodeServerFailure
|
||||||
|
if dnsErr.IsNotFound {
|
||||||
|
// Pass through NXDOMAIN
|
||||||
|
resp.Rcode = dns.RcodeNameError
|
||||||
|
}
|
||||||
|
|
||||||
|
if dnsErr.Server != "" {
|
||||||
|
log.Warnf("failed to resolve query for domain=%s server=%s: %v", domain, dnsErr.Server, err)
|
||||||
|
} else {
|
||||||
|
log.Warnf(errResolveFailed, domain, err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
resp.Rcode = dns.RcodeServerFailure
|
||||||
|
log.Warnf(errResolveFailed, domain, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.WriteMsg(resp); err != nil {
|
||||||
|
log.Errorf("failed to write failure DNS response: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range ips {
|
||||||
|
var respRecord dns.RR
|
||||||
|
if ip.To4() == nil {
|
||||||
|
log.Tracef("resolved domain=%s to IPv6=%s", domain, ip)
|
||||||
|
rr := dns.AAAA{
|
||||||
|
AAAA: ip,
|
||||||
|
Hdr: dns.RR_Header{
|
||||||
|
Name: domain,
|
||||||
|
Rrtype: dns.TypeAAAA,
|
||||||
|
Class: dns.ClassINET,
|
||||||
|
Ttl: f.ttl,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
respRecord = &rr
|
||||||
|
} else {
|
||||||
|
log.Tracef("resolved domain=%s to IPv4=%s", domain, ip)
|
||||||
|
rr := dns.A{
|
||||||
|
A: ip,
|
||||||
|
Hdr: dns.RR_Header{
|
||||||
|
Name: domain,
|
||||||
|
Rrtype: dns.TypeA,
|
||||||
|
Class: dns.ClassINET,
|
||||||
|
Ttl: f.ttl,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
respRecord = &rr
|
||||||
|
}
|
||||||
|
resp.Answer = append(resp.Answer, respRecord)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.WriteMsg(resp); err != nil {
|
||||||
|
log.Errorf("failed to write DNS response: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterDomains returns a list of normalized domains
|
||||||
|
func filterDomains(domains []string) []string {
|
||||||
|
newDomains := make([]string, 0, len(domains))
|
||||||
|
for _, d := range domains {
|
||||||
|
if d == "" {
|
||||||
|
log.Warn("empty domain in DNS forwarder")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newDomains = append(newDomains, nbdns.NormalizeZone(d))
|
||||||
|
}
|
||||||
|
return newDomains
|
||||||
|
}
|
106
client/internal/dnsfwd/manager.go
Normal file
106
client/internal/dnsfwd/manager.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package dnsfwd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
nberrors "github.com/netbirdio/netbird/client/errors"
|
||||||
|
firewall "github.com/netbirdio/netbird/client/firewall/manager"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ListenPort is the port that the DNS forwarder listens on. It has been used by the client peers also
|
||||||
|
ListenPort = 5353
|
||||||
|
dnsTTL = 60 //seconds
|
||||||
|
)
|
||||||
|
|
||||||
|
type Manager struct {
|
||||||
|
firewall firewall.Manager
|
||||||
|
|
||||||
|
fwRules []firewall.Rule
|
||||||
|
dnsForwarder *DNSForwarder
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManager(fw firewall.Manager) *Manager {
|
||||||
|
return &Manager{
|
||||||
|
firewall: fw,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) Start(domains []string) error {
|
||||||
|
log.Infof("starting DNS forwarder")
|
||||||
|
if m.dnsForwarder != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.allowDNSFirewall(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.dnsForwarder = NewDNSForwarder(fmt.Sprintf(":%d", ListenPort), dnsTTL)
|
||||||
|
go func() {
|
||||||
|
if err := m.dnsForwarder.Listen(domains); err != nil {
|
||||||
|
// todo handle close error if it is exists
|
||||||
|
log.Errorf("failed to start DNS forwarder, err: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) UpdateDomains(domains []string) {
|
||||||
|
if m.dnsForwarder == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m.dnsForwarder.UpdateDomains(domains)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) Stop(ctx context.Context) error {
|
||||||
|
if m.dnsForwarder == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var mErr *multierror.Error
|
||||||
|
if err := m.dropDNSFirewall(); err != nil {
|
||||||
|
mErr = multierror.Append(mErr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.dnsForwarder.Close(ctx); err != nil {
|
||||||
|
mErr = multierror.Append(mErr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.dnsForwarder = nil
|
||||||
|
return nberrors.FormatErrorOrNil(mErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Manager) allowDNSFirewall() error {
|
||||||
|
dport := &firewall.Port{
|
||||||
|
IsRange: false,
|
||||||
|
Values: []int{ListenPort},
|
||||||
|
}
|
||||||
|
dnsRules, err := h.firewall.AddPeerFiltering(net.ParseIP("0.0.0.0"), firewall.ProtocolUDP, nil, dport, firewall.RuleDirectionIN, firewall.ActionAccept, "", "")
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to add allow DNS router rules, err: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
h.fwRules = dnsRules
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Manager) dropDNSFirewall() error {
|
||||||
|
var mErr *multierror.Error
|
||||||
|
for _, rule := range h.fwRules {
|
||||||
|
if err := h.firewall.DeletePeerRule(rule); err != nil {
|
||||||
|
mErr = multierror.Append(mErr, fmt.Errorf("failed to delete DNS router rules, err: %v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h.fwRules = nil
|
||||||
|
return nberrors.FormatErrorOrNil(mErr)
|
||||||
|
}
|
@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@ -30,10 +29,12 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/iface/device"
|
"github.com/netbirdio/netbird/client/iface/device"
|
||||||
"github.com/netbirdio/netbird/client/internal/acl"
|
"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/dnsfwd"
|
||||||
"github.com/netbirdio/netbird/client/internal/networkmonitor"
|
"github.com/netbirdio/netbird/client/internal/networkmonitor"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer/guard"
|
"github.com/netbirdio/netbird/client/internal/peer/guard"
|
||||||
icemaker "github.com/netbirdio/netbird/client/internal/peer/ice"
|
icemaker "github.com/netbirdio/netbird/client/internal/peer/ice"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peerstore"
|
||||||
"github.com/netbirdio/netbird/client/internal/relay"
|
"github.com/netbirdio/netbird/client/internal/relay"
|
||||||
"github.com/netbirdio/netbird/client/internal/rosenpass"
|
"github.com/netbirdio/netbird/client/internal/rosenpass"
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager"
|
"github.com/netbirdio/netbird/client/internal/routemanager"
|
||||||
@ -117,7 +118,7 @@ type Engine struct {
|
|||||||
// mgmClient is a Management Service client
|
// mgmClient is a Management Service client
|
||||||
mgmClient mgm.Client
|
mgmClient mgm.Client
|
||||||
// peerConns is a map that holds all the peers that are known to this peer
|
// peerConns is a map that holds all the peers that are known to this peer
|
||||||
peerConns map[string]*peer.Conn
|
peerStore *peerstore.Store
|
||||||
|
|
||||||
beforePeerHook nbnet.AddHookFunc
|
beforePeerHook nbnet.AddHookFunc
|
||||||
afterPeerHook nbnet.RemoveHookFunc
|
afterPeerHook nbnet.RemoveHookFunc
|
||||||
@ -137,10 +138,6 @@ type Engine struct {
|
|||||||
TURNs []*stun.URI
|
TURNs []*stun.URI
|
||||||
stunTurn atomic.Value
|
stunTurn atomic.Value
|
||||||
|
|
||||||
// clientRoutes is the most recent list of clientRoutes received from the Management Service
|
|
||||||
clientRoutes route.HAMap
|
|
||||||
clientRoutesMu sync.RWMutex
|
|
||||||
|
|
||||||
clientCtx context.Context
|
clientCtx context.Context
|
||||||
clientCancel context.CancelFunc
|
clientCancel context.CancelFunc
|
||||||
|
|
||||||
@ -161,9 +158,10 @@ type Engine struct {
|
|||||||
|
|
||||||
statusRecorder *peer.Status
|
statusRecorder *peer.Status
|
||||||
|
|
||||||
firewall manager.Manager
|
firewall manager.Manager
|
||||||
routeManager routemanager.Manager
|
routeManager routemanager.Manager
|
||||||
acl acl.Manager
|
acl acl.Manager
|
||||||
|
dnsForwardMgr *dnsfwd.Manager
|
||||||
|
|
||||||
dnsServer dns.Server
|
dnsServer dns.Server
|
||||||
|
|
||||||
@ -234,7 +232,7 @@ func NewEngineWithProbes(
|
|||||||
signaler: peer.NewSignaler(signalClient, config.WgPrivateKey),
|
signaler: peer.NewSignaler(signalClient, config.WgPrivateKey),
|
||||||
mgmClient: mgmClient,
|
mgmClient: mgmClient,
|
||||||
relayManager: relayManager,
|
relayManager: relayManager,
|
||||||
peerConns: make(map[string]*peer.Conn),
|
peerStore: peerstore.NewConnStore(),
|
||||||
syncMsgMux: &sync.Mutex{},
|
syncMsgMux: &sync.Mutex{},
|
||||||
config: config,
|
config: config,
|
||||||
mobileDep: mobileDep,
|
mobileDep: mobileDep,
|
||||||
@ -287,6 +285,13 @@ func (e *Engine) Stop() error {
|
|||||||
e.routeManager.Stop(e.stateManager)
|
e.routeManager.Stop(e.stateManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if e.dnsForwardMgr != nil {
|
||||||
|
if err := e.dnsForwardMgr.Stop(context.Background()); err != nil {
|
||||||
|
log.Errorf("failed to stop DNS forward: %v", err)
|
||||||
|
}
|
||||||
|
e.dnsForwardMgr = nil
|
||||||
|
}
|
||||||
|
|
||||||
if e.srWatcher != nil {
|
if e.srWatcher != nil {
|
||||||
e.srWatcher.Close()
|
e.srWatcher.Close()
|
||||||
}
|
}
|
||||||
@ -300,10 +305,6 @@ func (e *Engine) Stop() error {
|
|||||||
return fmt.Errorf("failed to remove all peers: %s", err)
|
return fmt.Errorf("failed to remove all peers: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
e.clientRoutesMu.Lock()
|
|
||||||
e.clientRoutes = nil
|
|
||||||
e.clientRoutesMu.Unlock()
|
|
||||||
|
|
||||||
if e.cancel != nil {
|
if e.cancel != nil {
|
||||||
e.cancel()
|
e.cancel()
|
||||||
}
|
}
|
||||||
@ -382,6 +383,8 @@ func (e *Engine) Start() error {
|
|||||||
e.relayManager,
|
e.relayManager,
|
||||||
initialRoutes,
|
initialRoutes,
|
||||||
e.stateManager,
|
e.stateManager,
|
||||||
|
dnsServer,
|
||||||
|
e.peerStore,
|
||||||
)
|
)
|
||||||
beforePeerHook, afterPeerHook, err := e.routeManager.Init()
|
beforePeerHook, afterPeerHook, err := e.routeManager.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -460,8 +463,8 @@ func (e *Engine) modifyPeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
|||||||
var modified []*mgmProto.RemotePeerConfig
|
var modified []*mgmProto.RemotePeerConfig
|
||||||
for _, p := range peersUpdate {
|
for _, p := range peersUpdate {
|
||||||
peerPubKey := p.GetWgPubKey()
|
peerPubKey := p.GetWgPubKey()
|
||||||
if peerConn, ok := e.peerConns[peerPubKey]; ok {
|
if allowedIPs, ok := e.peerStore.AllowedIPs(peerPubKey); ok {
|
||||||
if peerConn.WgConfig().AllowedIps != strings.Join(p.AllowedIps, ",") {
|
if allowedIPs != strings.Join(p.AllowedIps, ",") {
|
||||||
modified = append(modified, p)
|
modified = append(modified, p)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -492,17 +495,12 @@ func (e *Engine) modifyPeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
|||||||
// removePeers finds and removes peers that do not exist anymore in the network map received from the Management Service.
|
// removePeers finds and removes peers that do not exist anymore in the network map received from the Management Service.
|
||||||
// It also removes peers that have been modified (e.g. change of IP address). They will be added again in addPeers method.
|
// It also removes peers that have been modified (e.g. change of IP address). They will be added again in addPeers method.
|
||||||
func (e *Engine) removePeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
func (e *Engine) removePeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
||||||
currentPeers := make([]string, 0, len(e.peerConns))
|
|
||||||
for p := range e.peerConns {
|
|
||||||
currentPeers = append(currentPeers, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
newPeers := make([]string, 0, len(peersUpdate))
|
newPeers := make([]string, 0, len(peersUpdate))
|
||||||
for _, p := range peersUpdate {
|
for _, p := range peersUpdate {
|
||||||
newPeers = append(newPeers, p.GetWgPubKey())
|
newPeers = append(newPeers, p.GetWgPubKey())
|
||||||
}
|
}
|
||||||
|
|
||||||
toRemove := util.SliceDiff(currentPeers, newPeers)
|
toRemove := util.SliceDiff(e.peerStore.PeersPubKey(), newPeers)
|
||||||
|
|
||||||
for _, p := range toRemove {
|
for _, p := range toRemove {
|
||||||
err := e.removePeer(p)
|
err := e.removePeer(p)
|
||||||
@ -516,7 +514,7 @@ func (e *Engine) removePeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
|||||||
|
|
||||||
func (e *Engine) removeAllPeers() error {
|
func (e *Engine) removeAllPeers() error {
|
||||||
log.Debugf("removing all peer connections")
|
log.Debugf("removing all peer connections")
|
||||||
for p := range e.peerConns {
|
for _, p := range e.peerStore.PeersPubKey() {
|
||||||
err := e.removePeer(p)
|
err := e.removePeer(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -540,9 +538,8 @@ func (e *Engine) removePeer(peerKey string) error {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
conn, exists := e.peerConns[peerKey]
|
conn, exists := e.peerStore.Remove(peerKey)
|
||||||
if exists {
|
if exists {
|
||||||
delete(e.peerConns, peerKey)
|
|
||||||
conn.Close()
|
conn.Close()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -786,7 +783,6 @@ func (e *Engine) updateTURNs(turns []*mgmProto.ProtectedHostConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
||||||
|
|
||||||
// intentionally leave it before checking serial because for now it can happen that peer IP changed but serial didn't
|
// intentionally leave it before checking serial because for now it can happen that peer IP changed but serial didn't
|
||||||
if networkMap.GetPeerConfig() != nil {
|
if networkMap.GetPeerConfig() != nil {
|
||||||
err := e.updateConfig(networkMap.GetPeerConfig())
|
err := e.updateConfig(networkMap.GetPeerConfig())
|
||||||
@ -806,20 +802,16 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
|||||||
e.acl.ApplyFiltering(networkMap)
|
e.acl.ApplyFiltering(networkMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
protoRoutes := networkMap.GetRoutes()
|
// DNS forwarder
|
||||||
if protoRoutes == nil {
|
dnsRouteFeatureFlag := toDNSFeatureFlag(networkMap)
|
||||||
protoRoutes = []*mgmProto.Route{}
|
dnsRouteDomains := toRouteDomains(e.config.WgPrivateKey.PublicKey().String(), networkMap.GetRoutes())
|
||||||
}
|
e.updateDNSForwarder(dnsRouteFeatureFlag, dnsRouteDomains)
|
||||||
|
|
||||||
_, clientRoutes, err := e.routeManager.UpdateRoutes(serial, toRoutes(protoRoutes))
|
routes := toRoutes(networkMap.GetRoutes())
|
||||||
if err != nil {
|
if err := e.routeManager.UpdateRoutes(serial, routes, dnsRouteFeatureFlag); err != nil {
|
||||||
log.Errorf("failed to update clientRoutes, err: %v", err)
|
log.Errorf("failed to update clientRoutes, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
e.clientRoutesMu.Lock()
|
|
||||||
e.clientRoutes = clientRoutes
|
|
||||||
e.clientRoutesMu.Unlock()
|
|
||||||
|
|
||||||
log.Debugf("got peers update from Management Service, total peers to connect to = %d", len(networkMap.GetRemotePeers()))
|
log.Debugf("got peers update from Management Service, total peers to connect to = %d", len(networkMap.GetRemotePeers()))
|
||||||
|
|
||||||
e.updateOfflinePeers(networkMap.GetOfflinePeers())
|
e.updateOfflinePeers(networkMap.GetOfflinePeers())
|
||||||
@ -867,8 +859,7 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
|||||||
protoDNSConfig = &mgmProto.DNSConfig{}
|
protoDNSConfig = &mgmProto.DNSConfig{}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = e.dnsServer.UpdateDNSServer(serial, toDNSConfig(protoDNSConfig))
|
if err := e.dnsServer.UpdateDNSServer(serial, toDNSConfig(protoDNSConfig)); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed to update dns server, err: %v", err)
|
log.Errorf("failed to update dns server, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -881,7 +872,18 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toDNSFeatureFlag(networkMap *mgmProto.NetworkMap) bool {
|
||||||
|
if networkMap.PeerConfig != nil {
|
||||||
|
return networkMap.PeerConfig.RoutingPeerDnsResolutionEnabled
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func toRoutes(protoRoutes []*mgmProto.Route) []*route.Route {
|
func toRoutes(protoRoutes []*mgmProto.Route) []*route.Route {
|
||||||
|
if protoRoutes == nil {
|
||||||
|
protoRoutes = []*mgmProto.Route{}
|
||||||
|
}
|
||||||
|
|
||||||
routes := make([]*route.Route, 0)
|
routes := make([]*route.Route, 0)
|
||||||
for _, protoRoute := range protoRoutes {
|
for _, protoRoute := range protoRoutes {
|
||||||
var prefix netip.Prefix
|
var prefix netip.Prefix
|
||||||
@ -892,6 +894,7 @@ func toRoutes(protoRoutes []*mgmProto.Route) []*route.Route {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
convertedRoute := &route.Route{
|
convertedRoute := &route.Route{
|
||||||
ID: route.ID(protoRoute.ID),
|
ID: route.ID(protoRoute.ID),
|
||||||
Network: prefix,
|
Network: prefix,
|
||||||
@ -908,6 +911,23 @@ func toRoutes(protoRoutes []*mgmProto.Route) []*route.Route {
|
|||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toRouteDomains(myPubKey string, protoRoutes []*mgmProto.Route) []string {
|
||||||
|
if protoRoutes == nil {
|
||||||
|
protoRoutes = []*mgmProto.Route{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dnsRoutes []string
|
||||||
|
for _, protoRoute := range protoRoutes {
|
||||||
|
if len(protoRoute.Domains) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if protoRoute.Peer == myPubKey {
|
||||||
|
dnsRoutes = append(dnsRoutes, protoRoute.Domains...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dnsRoutes
|
||||||
|
}
|
||||||
|
|
||||||
func toDNSConfig(protoDNSConfig *mgmProto.DNSConfig) nbdns.Config {
|
func toDNSConfig(protoDNSConfig *mgmProto.DNSConfig) nbdns.Config {
|
||||||
dnsUpdate := nbdns.Config{
|
dnsUpdate := nbdns.Config{
|
||||||
ServiceEnable: protoDNSConfig.GetServiceEnable(),
|
ServiceEnable: protoDNSConfig.GetServiceEnable(),
|
||||||
@ -982,12 +1002,16 @@ func (e *Engine) addNewPeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
|||||||
func (e *Engine) addNewPeer(peerConfig *mgmProto.RemotePeerConfig) error {
|
func (e *Engine) addNewPeer(peerConfig *mgmProto.RemotePeerConfig) error {
|
||||||
peerKey := peerConfig.GetWgPubKey()
|
peerKey := peerConfig.GetWgPubKey()
|
||||||
peerIPs := peerConfig.GetAllowedIps()
|
peerIPs := peerConfig.GetAllowedIps()
|
||||||
if _, ok := e.peerConns[peerKey]; !ok {
|
if _, ok := e.peerStore.PeerConn(peerKey); !ok {
|
||||||
conn, err := e.createPeerConn(peerKey, strings.Join(peerIPs, ","))
|
conn, err := e.createPeerConn(peerKey, strings.Join(peerIPs, ","))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create peer connection: %w", err)
|
return fmt.Errorf("create peer connection: %w", err)
|
||||||
}
|
}
|
||||||
e.peerConns[peerKey] = conn
|
|
||||||
|
if ok := e.peerStore.AddPeerConn(peerKey, conn); !ok {
|
||||||
|
conn.Close()
|
||||||
|
return fmt.Errorf("peer already exists: %s", peerKey)
|
||||||
|
}
|
||||||
|
|
||||||
if e.beforePeerHook != nil && e.afterPeerHook != nil {
|
if e.beforePeerHook != nil && e.afterPeerHook != nil {
|
||||||
conn.AddBeforeAddPeerHook(e.beforePeerHook)
|
conn.AddBeforeAddPeerHook(e.beforePeerHook)
|
||||||
@ -1076,8 +1100,8 @@ func (e *Engine) receiveSignalEvents() {
|
|||||||
e.syncMsgMux.Lock()
|
e.syncMsgMux.Lock()
|
||||||
defer e.syncMsgMux.Unlock()
|
defer e.syncMsgMux.Unlock()
|
||||||
|
|
||||||
conn := e.peerConns[msg.Key]
|
conn, ok := e.peerStore.PeerConn(msg.Key)
|
||||||
if conn == nil {
|
if !ok {
|
||||||
return fmt.Errorf("wrongly addressed message %s", msg.Key)
|
return fmt.Errorf("wrongly addressed message %s", msg.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1135,7 +1159,7 @@ func (e *Engine) receiveSignalEvents() {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
go conn.OnRemoteCandidate(candidate, e.GetClientRoutes())
|
go conn.OnRemoteCandidate(candidate, e.routeManager.GetClientRoutes())
|
||||||
case sProto.Body_MODE:
|
case sProto.Body_MODE:
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1322,26 +1346,6 @@ func (e *Engine) newDnsServer() ([]*route.Route, dns.Server, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClientRoutes returns the current routes from the route map
|
|
||||||
func (e *Engine) GetClientRoutes() route.HAMap {
|
|
||||||
e.clientRoutesMu.RLock()
|
|
||||||
defer e.clientRoutesMu.RUnlock()
|
|
||||||
|
|
||||||
return maps.Clone(e.clientRoutes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClientRoutesWithNetID returns the current routes from the route map, but the keys consist of the network ID only
|
|
||||||
func (e *Engine) GetClientRoutesWithNetID() map[route.NetID][]*route.Route {
|
|
||||||
e.clientRoutesMu.RLock()
|
|
||||||
defer e.clientRoutesMu.RUnlock()
|
|
||||||
|
|
||||||
routes := make(map[route.NetID][]*route.Route, len(e.clientRoutes))
|
|
||||||
for id, v := range e.clientRoutes {
|
|
||||||
routes[id.NetID()] = v
|
|
||||||
}
|
|
||||||
return routes
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRouteManager returns the route manager
|
// GetRouteManager returns the route manager
|
||||||
func (e *Engine) GetRouteManager() routemanager.Manager {
|
func (e *Engine) GetRouteManager() routemanager.Manager {
|
||||||
return e.routeManager
|
return e.routeManager
|
||||||
@ -1426,9 +1430,8 @@ func (e *Engine) receiveProbeEvents() {
|
|||||||
go e.probes.WgProbe.Receive(e.ctx, func() bool {
|
go e.probes.WgProbe.Receive(e.ctx, func() bool {
|
||||||
log.Debug("received wg probe request")
|
log.Debug("received wg probe request")
|
||||||
|
|
||||||
for _, peer := range e.peerConns {
|
for _, key := range e.peerStore.PeersPubKey() {
|
||||||
key := peer.GetKey()
|
wgStats, err := e.wgInterface.GetStats(key)
|
||||||
wgStats, err := peer.WgConfig().WgInterface.GetStats(key)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("failed to get wg stats for peer %s: %s", key, err)
|
log.Debugf("failed to get wg stats for peer %s: %s", key, err)
|
||||||
}
|
}
|
||||||
@ -1505,7 +1508,7 @@ func (e *Engine) startNetworkMonitor() {
|
|||||||
|
|
||||||
func (e *Engine) addrViaRoutes(addr netip.Addr) (bool, netip.Prefix, error) {
|
func (e *Engine) addrViaRoutes(addr netip.Addr) (bool, netip.Prefix, error) {
|
||||||
var vpnRoutes []netip.Prefix
|
var vpnRoutes []netip.Prefix
|
||||||
for _, routes := range e.GetClientRoutes() {
|
for _, routes := range e.routeManager.GetClientRoutes() {
|
||||||
if len(routes) > 0 && routes[0] != nil {
|
if len(routes) > 0 && routes[0] != nil {
|
||||||
vpnRoutes = append(vpnRoutes, routes[0].Network)
|
vpnRoutes = append(vpnRoutes, routes[0].Network)
|
||||||
}
|
}
|
||||||
@ -1573,6 +1576,40 @@ func (e *Engine) GetLatestNetworkMap() (*mgmProto.NetworkMap, error) {
|
|||||||
return nm, nil
|
return nm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateDNSForwarder start or stop the DNS forwarder based on the domains and the feature flag
|
||||||
|
func (e *Engine) updateDNSForwarder(enabled bool, domains []string) {
|
||||||
|
if !enabled {
|
||||||
|
if e.dnsForwardMgr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := e.dnsForwardMgr.Stop(context.Background()); err != nil {
|
||||||
|
log.Errorf("failed to stop DNS forward: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(domains) > 0 {
|
||||||
|
log.Infof("enable domain router service for domains: %v", domains)
|
||||||
|
if e.dnsForwardMgr == nil {
|
||||||
|
e.dnsForwardMgr = dnsfwd.NewManager(e.firewall)
|
||||||
|
|
||||||
|
if err := e.dnsForwardMgr.Start(domains); err != nil {
|
||||||
|
log.Errorf("failed to start DNS forward: %v", err)
|
||||||
|
e.dnsForwardMgr = nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Infof("update domain router service for domains: %v", domains)
|
||||||
|
e.dnsForwardMgr.UpdateDomains(domains)
|
||||||
|
}
|
||||||
|
} else if e.dnsForwardMgr != nil {
|
||||||
|
log.Infof("disable domain router service")
|
||||||
|
if err := e.dnsForwardMgr.Stop(context.Background()); err != nil {
|
||||||
|
log.Errorf("failed to stop DNS forward: %v", err)
|
||||||
|
}
|
||||||
|
e.dnsForwardMgr = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// isChecksEqual checks if two slices of checks are equal.
|
// isChecksEqual checks if two slices of checks are equal.
|
||||||
func isChecksEqual(checks []*mgmProto.Checks, oChecks []*mgmProto.Checks) bool {
|
func isChecksEqual(checks []*mgmProto.Checks, oChecks []*mgmProto.Checks) bool {
|
||||||
for _, check := range checks {
|
for _, check := range checks {
|
||||||
|
@ -39,6 +39,8 @@ import (
|
|||||||
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"
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
|
"github.com/netbirdio/netbird/management/server/settings"
|
||||||
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
relayClient "github.com/netbirdio/netbird/relay/client"
|
relayClient "github.com/netbirdio/netbird/relay/client"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
@ -251,7 +253,7 @@ func TestEngine_UpdateNetworkMap(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
engine.wgInterface = wgIface
|
engine.wgInterface = wgIface
|
||||||
engine.routeManager = routemanager.NewManager(ctx, key.PublicKey().String(), time.Minute, engine.wgInterface, engine.statusRecorder, relayMgr, nil, nil)
|
engine.routeManager = routemanager.NewManager(ctx, key.PublicKey().String(), time.Minute, engine.wgInterface, engine.statusRecorder, relayMgr, nil, nil, nil, nil)
|
||||||
_, _, err = engine.routeManager.Init()
|
_, _, err = engine.routeManager.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
engine.dnsServer = &dns.MockServer{
|
engine.dnsServer = &dns.MockServer{
|
||||||
@ -391,8 +393,8 @@ func TestEngine_UpdateNetworkMap(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(engine.peerConns) != c.expectedLen {
|
if len(engine.peerStore.PeersPubKey()) != c.expectedLen {
|
||||||
t.Errorf("expecting Engine.peerConns to be of size %d, got %d", c.expectedLen, len(engine.peerConns))
|
t.Errorf("expecting Engine.peerConns to be of size %d, got %d", c.expectedLen, len(engine.peerStore.PeersPubKey()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if engine.networkSerial != c.expectedSerial {
|
if engine.networkSerial != c.expectedSerial {
|
||||||
@ -400,7 +402,7 @@ func TestEngine_UpdateNetworkMap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range c.expectedPeers {
|
for _, p := range c.expectedPeers {
|
||||||
conn, ok := engine.peerConns[p.GetWgPubKey()]
|
conn, ok := engine.peerStore.PeerConn(p.GetWgPubKey())
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("expecting Engine.peerConns to contain peer %s", p)
|
t.Errorf("expecting Engine.peerConns to contain peer %s", p)
|
||||||
}
|
}
|
||||||
@ -625,10 +627,10 @@ func TestEngine_UpdateNetworkMapWithRoutes(t *testing.T) {
|
|||||||
}{}
|
}{}
|
||||||
|
|
||||||
mockRouteManager := &routemanager.MockManager{
|
mockRouteManager := &routemanager.MockManager{
|
||||||
UpdateRoutesFunc: func(updateSerial uint64, newRoutes []*route.Route) (map[route.ID]*route.Route, route.HAMap, error) {
|
UpdateRoutesFunc: func(updateSerial uint64, newRoutes []*route.Route) error {
|
||||||
input.inputSerial = updateSerial
|
input.inputSerial = updateSerial
|
||||||
input.inputRoutes = newRoutes
|
input.inputRoutes = newRoutes
|
||||||
return nil, nil, testCase.inputErr
|
return testCase.inputErr
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -801,8 +803,8 @@ func TestEngine_UpdateNetworkMapWithDNSUpdate(t *testing.T) {
|
|||||||
assert.NoError(t, err, "shouldn't return error")
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
mockRouteManager := &routemanager.MockManager{
|
mockRouteManager := &routemanager.MockManager{
|
||||||
UpdateRoutesFunc: func(updateSerial uint64, newRoutes []*route.Route) (map[route.ID]*route.Route, route.HAMap, error) {
|
UpdateRoutesFunc: func(updateSerial uint64, newRoutes []*route.Route) error {
|
||||||
return nil, nil, nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1196,7 +1198,7 @@ func startManagement(t *testing.T, dataDir, testFile string) (*grpc.Server, stri
|
|||||||
}
|
}
|
||||||
s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
|
s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
|
||||||
|
|
||||||
store, cleanUp, err := server.NewTestStoreFromSQL(context.Background(), testFile, config.Datadir)
|
store, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), testFile, config.Datadir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
@ -1218,7 +1220,7 @@ func startManagement(t *testing.T, dataDir, testFile string) (*grpc.Server, stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
|
secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
|
||||||
mgmtServer, err := server.NewServer(context.Background(), config, accountManager, peersUpdateManager, secretsManager, nil, nil)
|
mgmtServer, err := server.NewServer(context.Background(), config, accountManager, settings.NewManager(store), peersUpdateManager, secretsManager, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
@ -1237,7 +1239,8 @@ func getConnectedPeers(e *Engine) int {
|
|||||||
e.syncMsgMux.Lock()
|
e.syncMsgMux.Lock()
|
||||||
defer e.syncMsgMux.Unlock()
|
defer e.syncMsgMux.Unlock()
|
||||||
i := 0
|
i := 0
|
||||||
for _, conn := range e.peerConns {
|
for _, id := range e.peerStore.PeersPubKey() {
|
||||||
|
conn, _ := e.peerStore.PeerConn(id)
|
||||||
if conn.Status() == peer.StatusConnected {
|
if conn.Status() == peer.StatusConnected {
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
@ -1249,5 +1252,5 @@ func getPeers(e *Engine) int {
|
|||||||
e.syncMsgMux.Lock()
|
e.syncMsgMux.Lock()
|
||||||
defer e.syncMsgMux.Unlock()
|
defer e.syncMsgMux.Unlock()
|
||||||
|
|
||||||
return len(e.peerConns)
|
return len(e.peerStore.PeersPubKey())
|
||||||
}
|
}
|
||||||
|
@ -747,6 +747,11 @@ func (conn *Conn) setRelayedProxy(proxy wgproxy.Proxy) {
|
|||||||
conn.wgProxyRelay = proxy
|
conn.wgProxyRelay = proxy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllowedIP returns the allowed IP of the remote peer
|
||||||
|
func (conn *Conn) AllowedIP() net.IP {
|
||||||
|
return conn.allowedIP
|
||||||
|
}
|
||||||
|
|
||||||
func isController(config ConnConfig) bool {
|
func isController(config ConnConfig) bool {
|
||||||
return config.LocalKey > config.Key
|
return config.LocalKey > config.Key
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,11 @@ import (
|
|||||||
relayClient "github.com/netbirdio/netbird/relay/client"
|
relayClient "github.com/netbirdio/netbird/relay/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ResolvedDomainInfo struct {
|
||||||
|
Prefixes []netip.Prefix
|
||||||
|
ParentDomain domain.Domain
|
||||||
|
}
|
||||||
|
|
||||||
// State contains the latest state of a peer
|
// State contains the latest state of a peer
|
||||||
type State struct {
|
type State struct {
|
||||||
Mux *sync.RWMutex
|
Mux *sync.RWMutex
|
||||||
@ -138,7 +143,7 @@ type Status struct {
|
|||||||
rosenpassEnabled bool
|
rosenpassEnabled bool
|
||||||
rosenpassPermissive bool
|
rosenpassPermissive bool
|
||||||
nsGroupStates []NSGroupState
|
nsGroupStates []NSGroupState
|
||||||
resolvedDomainsStates map[domain.Domain][]netip.Prefix
|
resolvedDomainsStates map[domain.Domain]ResolvedDomainInfo
|
||||||
|
|
||||||
// To reduce the number of notification invocation this bool will be true when need to call the notification
|
// To reduce the number of notification invocation this bool will be true when need to call the notification
|
||||||
// Some Peer actions mostly used by in a batch when the network map has been synchronized. In these type of events
|
// Some Peer actions mostly used by in a batch when the network map has been synchronized. In these type of events
|
||||||
@ -156,7 +161,7 @@ func NewRecorder(mgmAddress string) *Status {
|
|||||||
offlinePeers: make([]State, 0),
|
offlinePeers: make([]State, 0),
|
||||||
notifier: newNotifier(),
|
notifier: newNotifier(),
|
||||||
mgmAddress: mgmAddress,
|
mgmAddress: mgmAddress,
|
||||||
resolvedDomainsStates: make(map[domain.Domain][]netip.Prefix),
|
resolvedDomainsStates: map[domain.Domain]ResolvedDomainInfo{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,16 +596,27 @@ func (d *Status) UpdateDNSStates(dnsStates []NSGroupState) {
|
|||||||
d.nsGroupStates = dnsStates
|
d.nsGroupStates = dnsStates
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Status) UpdateResolvedDomainsStates(domain domain.Domain, prefixes []netip.Prefix) {
|
func (d *Status) UpdateResolvedDomainsStates(originalDomain domain.Domain, resolvedDomain domain.Domain, prefixes []netip.Prefix) {
|
||||||
d.mux.Lock()
|
d.mux.Lock()
|
||||||
defer d.mux.Unlock()
|
defer d.mux.Unlock()
|
||||||
d.resolvedDomainsStates[domain] = prefixes
|
|
||||||
|
// Store both the original domain pattern and resolved domain
|
||||||
|
d.resolvedDomainsStates[resolvedDomain] = ResolvedDomainInfo{
|
||||||
|
Prefixes: prefixes,
|
||||||
|
ParentDomain: originalDomain,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Status) DeleteResolvedDomainsStates(domain domain.Domain) {
|
func (d *Status) DeleteResolvedDomainsStates(domain domain.Domain) {
|
||||||
d.mux.Lock()
|
d.mux.Lock()
|
||||||
defer d.mux.Unlock()
|
defer d.mux.Unlock()
|
||||||
delete(d.resolvedDomainsStates, domain)
|
|
||||||
|
// Remove all entries that have this domain as their parent
|
||||||
|
for k, v := range d.resolvedDomainsStates {
|
||||||
|
if v.ParentDomain == domain {
|
||||||
|
delete(d.resolvedDomainsStates, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Status) GetRosenpassState() RosenpassState {
|
func (d *Status) GetRosenpassState() RosenpassState {
|
||||||
@ -702,7 +718,7 @@ func (d *Status) GetDNSStates() []NSGroupState {
|
|||||||
return d.nsGroupStates
|
return d.nsGroupStates
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Status) GetResolvedDomainsStates() map[domain.Domain][]netip.Prefix {
|
func (d *Status) GetResolvedDomainsStates() map[domain.Domain]ResolvedDomainInfo {
|
||||||
d.mux.Lock()
|
d.mux.Lock()
|
||||||
defer d.mux.Unlock()
|
defer d.mux.Unlock()
|
||||||
return maps.Clone(d.resolvedDomainsStates)
|
return maps.Clone(d.resolvedDomainsStates)
|
||||||
|
87
client/internal/peerstore/store.go
Normal file
87
client/internal/peerstore/store.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package peerstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Store is a thread-safe store for peer connections.
|
||||||
|
type Store struct {
|
||||||
|
peerConns map[string]*peer.Conn
|
||||||
|
peerConnsMu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnStore() *Store {
|
||||||
|
return &Store{
|
||||||
|
peerConns: make(map[string]*peer.Conn),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) AddPeerConn(pubKey string, conn *peer.Conn) bool {
|
||||||
|
s.peerConnsMu.Lock()
|
||||||
|
defer s.peerConnsMu.Unlock()
|
||||||
|
|
||||||
|
_, ok := s.peerConns[pubKey]
|
||||||
|
if ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
s.peerConns[pubKey] = conn
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) Remove(pubKey string) (*peer.Conn, bool) {
|
||||||
|
s.peerConnsMu.Lock()
|
||||||
|
defer s.peerConnsMu.Unlock()
|
||||||
|
|
||||||
|
p, ok := s.peerConns[pubKey]
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
delete(s.peerConns, pubKey)
|
||||||
|
return p, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) AllowedIPs(pubKey string) (string, bool) {
|
||||||
|
s.peerConnsMu.RLock()
|
||||||
|
defer s.peerConnsMu.RUnlock()
|
||||||
|
|
||||||
|
p, ok := s.peerConns[pubKey]
|
||||||
|
if !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return p.WgConfig().AllowedIps, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) AllowedIP(pubKey string) (net.IP, bool) {
|
||||||
|
s.peerConnsMu.RLock()
|
||||||
|
defer s.peerConnsMu.RUnlock()
|
||||||
|
|
||||||
|
p, ok := s.peerConns[pubKey]
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return p.AllowedIP(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) PeerConn(pubKey string) (*peer.Conn, bool) {
|
||||||
|
s.peerConnsMu.RLock()
|
||||||
|
defer s.peerConnsMu.RUnlock()
|
||||||
|
|
||||||
|
p, ok := s.peerConns[pubKey]
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return p, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) PeersPubKey() []string {
|
||||||
|
s.peerConnsMu.RLock()
|
||||||
|
defer s.peerConnsMu.RUnlock()
|
||||||
|
|
||||||
|
return maps.Keys(s.peerConns)
|
||||||
|
}
|
@ -13,12 +13,20 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/iface"
|
"github.com/netbirdio/netbird/client/iface"
|
||||||
nbdns "github.com/netbirdio/netbird/client/internal/dns"
|
nbdns "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/peerstore"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/routemanager/dnsinterceptor"
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager/dynamic"
|
"github.com/netbirdio/netbird/client/internal/routemanager/dynamic"
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager/refcounter"
|
"github.com/netbirdio/netbird/client/internal/routemanager/refcounter"
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager/static"
|
"github.com/netbirdio/netbird/client/internal/routemanager/static"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
handlerTypeDynamic = iota
|
||||||
|
handlerTypeDomain
|
||||||
|
handlerTypeStatic
|
||||||
|
)
|
||||||
|
|
||||||
type routerPeerStatus struct {
|
type routerPeerStatus struct {
|
||||||
connected bool
|
connected bool
|
||||||
relayed bool
|
relayed bool
|
||||||
@ -53,7 +61,18 @@ type clientNetwork struct {
|
|||||||
updateSerial uint64
|
updateSerial uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func newClientNetworkWatcher(ctx context.Context, dnsRouteInterval time.Duration, wgInterface iface.IWGIface, statusRecorder *peer.Status, rt *route.Route, routeRefCounter *refcounter.RouteRefCounter, allowedIPsRefCounter *refcounter.AllowedIPsRefCounter) *clientNetwork {
|
func newClientNetworkWatcher(
|
||||||
|
ctx context.Context,
|
||||||
|
dnsRouteInterval time.Duration,
|
||||||
|
wgInterface iface.IWGIface,
|
||||||
|
statusRecorder *peer.Status,
|
||||||
|
rt *route.Route,
|
||||||
|
routeRefCounter *refcounter.RouteRefCounter,
|
||||||
|
allowedIPsRefCounter *refcounter.AllowedIPsRefCounter,
|
||||||
|
dnsServer nbdns.Server,
|
||||||
|
peerStore *peerstore.Store,
|
||||||
|
useNewDNSRoute bool,
|
||||||
|
) *clientNetwork {
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
client := &clientNetwork{
|
client := &clientNetwork{
|
||||||
@ -65,7 +84,17 @@ func newClientNetworkWatcher(ctx context.Context, dnsRouteInterval time.Duration
|
|||||||
routePeersNotifiers: make(map[string]chan struct{}),
|
routePeersNotifiers: make(map[string]chan struct{}),
|
||||||
routeUpdate: make(chan routesUpdate),
|
routeUpdate: make(chan routesUpdate),
|
||||||
peerStateUpdate: make(chan struct{}),
|
peerStateUpdate: make(chan struct{}),
|
||||||
handler: handlerFromRoute(rt, routeRefCounter, allowedIPsRefCounter, dnsRouteInterval, statusRecorder, wgInterface),
|
handler: handlerFromRoute(
|
||||||
|
rt,
|
||||||
|
routeRefCounter,
|
||||||
|
allowedIPsRefCounter,
|
||||||
|
dnsRouteInterval,
|
||||||
|
statusRecorder,
|
||||||
|
wgInterface,
|
||||||
|
dnsServer,
|
||||||
|
peerStore,
|
||||||
|
useNewDNSRoute,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
@ -368,10 +397,50 @@ func (c *clientNetwork) peersStateAndUpdateWatcher() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlerFromRoute(rt *route.Route, routeRefCounter *refcounter.RouteRefCounter, allowedIPsRefCounter *refcounter.AllowedIPsRefCounter, dnsRouterInteval time.Duration, statusRecorder *peer.Status, wgInterface iface.IWGIface) RouteHandler {
|
func handlerFromRoute(
|
||||||
if rt.IsDynamic() {
|
rt *route.Route,
|
||||||
|
routeRefCounter *refcounter.RouteRefCounter,
|
||||||
|
allowedIPsRefCounter *refcounter.AllowedIPsRefCounter,
|
||||||
|
dnsRouterInteval time.Duration,
|
||||||
|
statusRecorder *peer.Status,
|
||||||
|
wgInterface iface.IWGIface,
|
||||||
|
dnsServer nbdns.Server,
|
||||||
|
peerStore *peerstore.Store,
|
||||||
|
useNewDNSRoute bool,
|
||||||
|
) RouteHandler {
|
||||||
|
switch handlerType(rt, useNewDNSRoute) {
|
||||||
|
case handlerTypeDomain:
|
||||||
|
return dnsinterceptor.New(
|
||||||
|
rt,
|
||||||
|
routeRefCounter,
|
||||||
|
allowedIPsRefCounter,
|
||||||
|
statusRecorder,
|
||||||
|
dnsServer,
|
||||||
|
peerStore,
|
||||||
|
)
|
||||||
|
case handlerTypeDynamic:
|
||||||
dns := nbdns.NewServiceViaMemory(wgInterface)
|
dns := nbdns.NewServiceViaMemory(wgInterface)
|
||||||
return dynamic.NewRoute(rt, routeRefCounter, allowedIPsRefCounter, dnsRouterInteval, statusRecorder, wgInterface, fmt.Sprintf("%s:%d", dns.RuntimeIP(), dns.RuntimePort()))
|
return dynamic.NewRoute(
|
||||||
|
rt,
|
||||||
|
routeRefCounter,
|
||||||
|
allowedIPsRefCounter,
|
||||||
|
dnsRouterInteval,
|
||||||
|
statusRecorder,
|
||||||
|
wgInterface,
|
||||||
|
fmt.Sprintf("%s:%d", dns.RuntimeIP(), dns.RuntimePort()),
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return static.NewRoute(rt, routeRefCounter, allowedIPsRefCounter)
|
||||||
}
|
}
|
||||||
return static.NewRoute(rt, routeRefCounter, allowedIPsRefCounter)
|
}
|
||||||
|
|
||||||
|
func handlerType(rt *route.Route, useNewDNSRoute bool) int {
|
||||||
|
if !rt.IsDynamic() {
|
||||||
|
return handlerTypeStatic
|
||||||
|
}
|
||||||
|
|
||||||
|
if useNewDNSRoute {
|
||||||
|
return handlerTypeDomain
|
||||||
|
}
|
||||||
|
return handlerTypeDynamic
|
||||||
}
|
}
|
||||||
|
356
client/internal/routemanager/dnsinterceptor/handler.go
Normal file
356
client/internal/routemanager/dnsinterceptor/handler.go
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
package dnsinterceptor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
nberrors "github.com/netbirdio/netbird/client/errors"
|
||||||
|
nbdns "github.com/netbirdio/netbird/client/internal/dns"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/dnsfwd"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peerstore"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/routemanager/refcounter"
|
||||||
|
"github.com/netbirdio/netbird/management/domain"
|
||||||
|
"github.com/netbirdio/netbird/route"
|
||||||
|
)
|
||||||
|
|
||||||
|
type domainMap map[domain.Domain][]netip.Prefix
|
||||||
|
|
||||||
|
type DnsInterceptor struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
route *route.Route
|
||||||
|
routeRefCounter *refcounter.RouteRefCounter
|
||||||
|
allowedIPsRefcounter *refcounter.AllowedIPsRefCounter
|
||||||
|
statusRecorder *peer.Status
|
||||||
|
dnsServer nbdns.Server
|
||||||
|
currentPeerKey string
|
||||||
|
interceptedDomains domainMap
|
||||||
|
peerStore *peerstore.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(
|
||||||
|
rt *route.Route,
|
||||||
|
routeRefCounter *refcounter.RouteRefCounter,
|
||||||
|
allowedIPsRefCounter *refcounter.AllowedIPsRefCounter,
|
||||||
|
statusRecorder *peer.Status,
|
||||||
|
dnsServer nbdns.Server,
|
||||||
|
peerStore *peerstore.Store,
|
||||||
|
) *DnsInterceptor {
|
||||||
|
return &DnsInterceptor{
|
||||||
|
route: rt,
|
||||||
|
routeRefCounter: routeRefCounter,
|
||||||
|
allowedIPsRefcounter: allowedIPsRefCounter,
|
||||||
|
statusRecorder: statusRecorder,
|
||||||
|
dnsServer: dnsServer,
|
||||||
|
interceptedDomains: make(domainMap),
|
||||||
|
peerStore: peerStore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DnsInterceptor) String() string {
|
||||||
|
return d.route.Domains.SafeString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DnsInterceptor) AddRoute(context.Context) error {
|
||||||
|
d.dnsServer.RegisterHandler(d.route.Domains.ToPunycodeList(), d, nbdns.PriorityDNSRoute)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DnsInterceptor) RemoveRoute() error {
|
||||||
|
d.mu.Lock()
|
||||||
|
|
||||||
|
var merr *multierror.Error
|
||||||
|
for domain, prefixes := range d.interceptedDomains {
|
||||||
|
for _, prefix := range prefixes {
|
||||||
|
if _, err := d.routeRefCounter.Decrement(prefix); err != nil {
|
||||||
|
merr = multierror.Append(merr, fmt.Errorf("remove dynamic route for IP %s: %v", prefix, err))
|
||||||
|
}
|
||||||
|
if d.currentPeerKey != "" {
|
||||||
|
if _, err := d.allowedIPsRefcounter.Decrement(prefix); err != nil {
|
||||||
|
merr = multierror.Append(merr, fmt.Errorf("remove allowed IP %s: %v", prefix, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Debugf("removed dynamic route(s) for [%s]: %s", domain.SafeString(), strings.ReplaceAll(fmt.Sprintf("%s", prefixes), " ", ", "))
|
||||||
|
|
||||||
|
}
|
||||||
|
for _, domain := range d.route.Domains {
|
||||||
|
d.statusRecorder.DeleteResolvedDomainsStates(domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(d.interceptedDomains)
|
||||||
|
d.mu.Unlock()
|
||||||
|
|
||||||
|
d.dnsServer.DeregisterHandler(d.route.Domains.ToPunycodeList(), nbdns.PriorityDNSRoute)
|
||||||
|
|
||||||
|
return nberrors.FormatErrorOrNil(merr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DnsInterceptor) AddAllowedIPs(peerKey string) error {
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
|
||||||
|
var merr *multierror.Error
|
||||||
|
for domain, prefixes := range d.interceptedDomains {
|
||||||
|
for _, prefix := range prefixes {
|
||||||
|
if ref, err := d.allowedIPsRefcounter.Increment(prefix, peerKey); err != nil {
|
||||||
|
merr = multierror.Append(merr, fmt.Errorf("add allowed IP %s: %v", prefix, err))
|
||||||
|
} else if ref.Count > 1 && ref.Out != peerKey {
|
||||||
|
log.Warnf("IP [%s] for domain [%s] is already routed by peer [%s]. HA routing disabled",
|
||||||
|
prefix.Addr(),
|
||||||
|
domain.SafeString(),
|
||||||
|
ref.Out,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.currentPeerKey = peerKey
|
||||||
|
return nberrors.FormatErrorOrNil(merr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DnsInterceptor) RemoveAllowedIPs() error {
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
|
||||||
|
var merr *multierror.Error
|
||||||
|
for _, prefixes := range d.interceptedDomains {
|
||||||
|
for _, prefix := range prefixes {
|
||||||
|
if _, err := d.allowedIPsRefcounter.Decrement(prefix); err != nil {
|
||||||
|
merr = multierror.Append(merr, fmt.Errorf("remove allowed IP %s: %v", prefix, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.currentPeerKey = ""
|
||||||
|
return nberrors.FormatErrorOrNil(merr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeDNS implements the dns.Handler interface
|
||||||
|
func (d *DnsInterceptor) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||||
|
if len(r.Question) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Tracef("received DNS request for domain=%s type=%v class=%v",
|
||||||
|
r.Question[0].Name, r.Question[0].Qtype, r.Question[0].Qclass)
|
||||||
|
|
||||||
|
d.mu.RLock()
|
||||||
|
peerKey := d.currentPeerKey
|
||||||
|
d.mu.RUnlock()
|
||||||
|
|
||||||
|
if peerKey == "" {
|
||||||
|
log.Tracef("no current peer key set, letting next handler try for domain=%s", r.Question[0].Name)
|
||||||
|
|
||||||
|
d.continueToNextHandler(w, r, "no current peer key")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
upstreamIP, err := d.getUpstreamIP(peerKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to get upstream IP: %v", err)
|
||||||
|
d.continueToNextHandler(w, r, fmt.Sprintf("failed to get upstream IP: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &dns.Client{
|
||||||
|
Timeout: 5 * time.Second,
|
||||||
|
Net: "udp",
|
||||||
|
}
|
||||||
|
upstream := fmt.Sprintf("%s:%d", upstreamIP, dnsfwd.ListenPort)
|
||||||
|
reply, _, err := client.ExchangeContext(context.Background(), r, upstream)
|
||||||
|
|
||||||
|
var answer []dns.RR
|
||||||
|
if reply != nil {
|
||||||
|
answer = reply.Answer
|
||||||
|
}
|
||||||
|
log.Tracef("upstream %s (%s) DNS response for domain=%s answers=%v", upstreamIP, peerKey, r.Question[0].Name, answer)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to exchange DNS request with %s: %v", upstream, err)
|
||||||
|
if err := w.WriteMsg(&dns.Msg{MsgHdr: dns.MsgHdr{Rcode: dns.RcodeServerFailure, Id: r.Id}}); err != nil {
|
||||||
|
log.Errorf("failed writing DNS response: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reply.Id = r.Id
|
||||||
|
if err := d.writeMsg(w, reply); err != nil {
|
||||||
|
log.Errorf("failed writing DNS response: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// continueToNextHandler signals the handler chain to try the next handler
|
||||||
|
func (d *DnsInterceptor) continueToNextHandler(w dns.ResponseWriter, r *dns.Msg, reason string) {
|
||||||
|
log.Tracef("continuing to next handler for domain=%s reason=%s", r.Question[0].Name, reason)
|
||||||
|
|
||||||
|
resp := new(dns.Msg)
|
||||||
|
resp.SetRcode(r, dns.RcodeNameError)
|
||||||
|
// Set Zero bit to signal handler chain to continue
|
||||||
|
resp.MsgHdr.Zero = true
|
||||||
|
if err := w.WriteMsg(resp); err != nil {
|
||||||
|
log.Errorf("failed writing DNS continue response: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DnsInterceptor) getUpstreamIP(peerKey string) (net.IP, error) {
|
||||||
|
peerAllowedIP, exists := d.peerStore.AllowedIP(peerKey)
|
||||||
|
if !exists {
|
||||||
|
return nil, fmt.Errorf("peer connection not found for key: %s", peerKey)
|
||||||
|
}
|
||||||
|
return peerAllowedIP, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DnsInterceptor) writeMsg(w dns.ResponseWriter, r *dns.Msg) error {
|
||||||
|
if r == nil {
|
||||||
|
return fmt.Errorf("received nil DNS message")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(r.Answer) > 0 && len(r.Question) > 0 {
|
||||||
|
origPattern := ""
|
||||||
|
if writer, ok := w.(*nbdns.ResponseWriterChain); ok {
|
||||||
|
origPattern = writer.GetOrigPattern()
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedDomain := domain.Domain(r.Question[0].Name)
|
||||||
|
|
||||||
|
// already punycode via RegisterHandler()
|
||||||
|
originalDomain := domain.Domain(origPattern)
|
||||||
|
if originalDomain == "" {
|
||||||
|
originalDomain = resolvedDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
var newPrefixes []netip.Prefix
|
||||||
|
for _, answer := range r.Answer {
|
||||||
|
var ip netip.Addr
|
||||||
|
switch rr := answer.(type) {
|
||||||
|
case *dns.A:
|
||||||
|
addr, ok := netip.AddrFromSlice(rr.A)
|
||||||
|
if !ok {
|
||||||
|
log.Tracef("failed to convert A record for domain=%s ip=%v", resolvedDomain, rr.A)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ip = addr
|
||||||
|
case *dns.AAAA:
|
||||||
|
addr, ok := netip.AddrFromSlice(rr.AAAA)
|
||||||
|
if !ok {
|
||||||
|
log.Tracef("failed to convert AAAA record for domain=%s ip=%v", resolvedDomain, rr.AAAA)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ip = addr
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix := netip.PrefixFrom(ip, ip.BitLen())
|
||||||
|
newPrefixes = append(newPrefixes, prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(newPrefixes) > 0 {
|
||||||
|
if err := d.updateDomainPrefixes(resolvedDomain, originalDomain, newPrefixes); err != nil {
|
||||||
|
log.Errorf("failed to update domain prefixes: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.WriteMsg(r); err != nil {
|
||||||
|
return fmt.Errorf("failed to write DNS response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DnsInterceptor) updateDomainPrefixes(resolvedDomain, originalDomain domain.Domain, newPrefixes []netip.Prefix) error {
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
|
||||||
|
oldPrefixes := d.interceptedDomains[resolvedDomain]
|
||||||
|
toAdd, toRemove := determinePrefixChanges(oldPrefixes, newPrefixes)
|
||||||
|
|
||||||
|
var merr *multierror.Error
|
||||||
|
|
||||||
|
// Add new prefixes
|
||||||
|
for _, prefix := range toAdd {
|
||||||
|
if _, err := d.routeRefCounter.Increment(prefix, struct{}{}); err != nil {
|
||||||
|
merr = multierror.Append(merr, fmt.Errorf("add route for IP %s: %v", prefix, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.currentPeerKey == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ref, err := d.allowedIPsRefcounter.Increment(prefix, d.currentPeerKey); err != nil {
|
||||||
|
merr = multierror.Append(merr, fmt.Errorf("add allowed IP %s: %v", prefix, err))
|
||||||
|
} else if ref.Count > 1 && ref.Out != d.currentPeerKey {
|
||||||
|
log.Warnf("IP [%s] for domain [%s] is already routed by peer [%s]. HA routing disabled",
|
||||||
|
prefix.Addr(),
|
||||||
|
resolvedDomain.SafeString(),
|
||||||
|
ref.Out,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !d.route.KeepRoute {
|
||||||
|
// Remove old prefixes
|
||||||
|
for _, prefix := range toRemove {
|
||||||
|
if _, err := d.routeRefCounter.Decrement(prefix); err != nil {
|
||||||
|
merr = multierror.Append(merr, fmt.Errorf("remove route for IP %s: %v", prefix, err))
|
||||||
|
}
|
||||||
|
if d.currentPeerKey != "" {
|
||||||
|
if _, err := d.allowedIPsRefcounter.Decrement(prefix); err != nil {
|
||||||
|
merr = multierror.Append(merr, fmt.Errorf("remove allowed IP %s: %v", prefix, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update domain prefixes using resolved domain as key
|
||||||
|
if len(toAdd) > 0 || len(toRemove) > 0 {
|
||||||
|
d.interceptedDomains[resolvedDomain] = newPrefixes
|
||||||
|
originalDomain = domain.Domain(strings.TrimSuffix(string(originalDomain), "."))
|
||||||
|
d.statusRecorder.UpdateResolvedDomainsStates(originalDomain, resolvedDomain, newPrefixes)
|
||||||
|
|
||||||
|
if len(toAdd) > 0 {
|
||||||
|
log.Debugf("added dynamic route(s) for domain=%s (pattern: domain=%s): %s",
|
||||||
|
resolvedDomain.SafeString(),
|
||||||
|
originalDomain.SafeString(),
|
||||||
|
toAdd)
|
||||||
|
}
|
||||||
|
if len(toRemove) > 0 {
|
||||||
|
log.Debugf("removed dynamic route(s) for domain=%s (pattern: domain=%s): %s",
|
||||||
|
resolvedDomain.SafeString(),
|
||||||
|
originalDomain.SafeString(),
|
||||||
|
toRemove)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nberrors.FormatErrorOrNil(merr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func determinePrefixChanges(oldPrefixes, newPrefixes []netip.Prefix) (toAdd, toRemove []netip.Prefix) {
|
||||||
|
prefixSet := make(map[netip.Prefix]bool)
|
||||||
|
for _, prefix := range oldPrefixes {
|
||||||
|
prefixSet[prefix] = false
|
||||||
|
}
|
||||||
|
for _, prefix := range newPrefixes {
|
||||||
|
if _, exists := prefixSet[prefix]; exists {
|
||||||
|
prefixSet[prefix] = true
|
||||||
|
} else {
|
||||||
|
toAdd = append(toAdd, prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for prefix, inUse := range prefixSet {
|
||||||
|
if !inUse {
|
||||||
|
toRemove = append(toRemove, prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -74,11 +74,7 @@ func NewRoute(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Route) String() string {
|
func (r *Route) String() string {
|
||||||
s, err := r.route.Domains.String()
|
return r.route.Domains.SafeString()
|
||||||
if err != nil {
|
|
||||||
return r.route.Domains.PunycodeString()
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Route) AddRoute(ctx context.Context) error {
|
func (r *Route) AddRoute(ctx context.Context) error {
|
||||||
@ -292,7 +288,7 @@ func (r *Route) updateDynamicRoutes(ctx context.Context, newDomains domainMap) e
|
|||||||
updatedPrefixes := combinePrefixes(oldPrefixes, removedPrefixes, addedPrefixes)
|
updatedPrefixes := combinePrefixes(oldPrefixes, removedPrefixes, addedPrefixes)
|
||||||
r.dynamicDomains[domain] = updatedPrefixes
|
r.dynamicDomains[domain] = updatedPrefixes
|
||||||
|
|
||||||
r.statusRecorder.UpdateResolvedDomainsStates(domain, updatedPrefixes)
|
r.statusRecorder.UpdateResolvedDomainsStates(domain, domain, updatedPrefixes)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nberrors.FormatErrorOrNil(merr)
|
return nberrors.FormatErrorOrNil(merr)
|
||||||
|
@ -12,12 +12,15 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
firewall "github.com/netbirdio/netbird/client/firewall/manager"
|
firewall "github.com/netbirdio/netbird/client/firewall/manager"
|
||||||
"github.com/netbirdio/netbird/client/iface"
|
"github.com/netbirdio/netbird/client/iface"
|
||||||
"github.com/netbirdio/netbird/client/iface/configurer"
|
"github.com/netbirdio/netbird/client/iface/configurer"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/dns"
|
||||||
"github.com/netbirdio/netbird/client/internal/listener"
|
"github.com/netbirdio/netbird/client/internal/listener"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peerstore"
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager/notifier"
|
"github.com/netbirdio/netbird/client/internal/routemanager/notifier"
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager/refcounter"
|
"github.com/netbirdio/netbird/client/internal/routemanager/refcounter"
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager/systemops"
|
"github.com/netbirdio/netbird/client/internal/routemanager/systemops"
|
||||||
@ -33,9 +36,11 @@ import (
|
|||||||
// Manager is a route manager interface
|
// Manager is a route manager interface
|
||||||
type Manager interface {
|
type Manager interface {
|
||||||
Init() (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error)
|
Init() (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error)
|
||||||
UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) (map[route.ID]*route.Route, route.HAMap, error)
|
UpdateRoutes(updateSerial uint64, newRoutes []*route.Route, useNewDNSRoute bool) error
|
||||||
TriggerSelection(route.HAMap)
|
TriggerSelection(route.HAMap)
|
||||||
GetRouteSelector() *routeselector.RouteSelector
|
GetRouteSelector() *routeselector.RouteSelector
|
||||||
|
GetClientRoutes() route.HAMap
|
||||||
|
GetClientRoutesWithNetID() map[route.NetID][]*route.Route
|
||||||
SetRouteChangeListener(listener listener.NetworkChangeListener)
|
SetRouteChangeListener(listener listener.NetworkChangeListener)
|
||||||
InitialRouteRange() []string
|
InitialRouteRange() []string
|
||||||
EnableServerRouter(firewall firewall.Manager) error
|
EnableServerRouter(firewall firewall.Manager) error
|
||||||
@ -60,6 +65,11 @@ type DefaultManager struct {
|
|||||||
allowedIPsRefCounter *refcounter.AllowedIPsRefCounter
|
allowedIPsRefCounter *refcounter.AllowedIPsRefCounter
|
||||||
dnsRouteInterval time.Duration
|
dnsRouteInterval time.Duration
|
||||||
stateManager *statemanager.Manager
|
stateManager *statemanager.Manager
|
||||||
|
// clientRoutes is the most recent list of clientRoutes received from the Management Service
|
||||||
|
clientRoutes route.HAMap
|
||||||
|
dnsServer dns.Server
|
||||||
|
peerStore *peerstore.Store
|
||||||
|
useNewDNSRoute bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewManager(
|
func NewManager(
|
||||||
@ -71,6 +81,8 @@ func NewManager(
|
|||||||
relayMgr *relayClient.Manager,
|
relayMgr *relayClient.Manager,
|
||||||
initialRoutes []*route.Route,
|
initialRoutes []*route.Route,
|
||||||
stateManager *statemanager.Manager,
|
stateManager *statemanager.Manager,
|
||||||
|
dnsServer dns.Server,
|
||||||
|
peerStore *peerstore.Store,
|
||||||
) *DefaultManager {
|
) *DefaultManager {
|
||||||
mCTX, cancel := context.WithCancel(ctx)
|
mCTX, cancel := context.WithCancel(ctx)
|
||||||
notifier := notifier.NewNotifier()
|
notifier := notifier.NewNotifier()
|
||||||
@ -88,6 +100,8 @@ func NewManager(
|
|||||||
pubKey: pubKey,
|
pubKey: pubKey,
|
||||||
notifier: notifier,
|
notifier: notifier,
|
||||||
stateManager: stateManager,
|
stateManager: stateManager,
|
||||||
|
dnsServer: dnsServer,
|
||||||
|
peerStore: peerStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
dm.routeRefCounter = refcounter.New(
|
dm.routeRefCounter = refcounter.New(
|
||||||
@ -116,7 +130,7 @@ func NewManager(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if runtime.GOOS == "android" {
|
if runtime.GOOS == "android" {
|
||||||
cr := dm.clientRoutes(initialRoutes)
|
cr := dm.initialClientRoutes(initialRoutes)
|
||||||
dm.notifier.SetInitialClientRoutes(cr)
|
dm.notifier.SetInitialClientRoutes(cr)
|
||||||
}
|
}
|
||||||
return dm
|
return dm
|
||||||
@ -207,33 +221,41 @@ func (m *DefaultManager) Stop(stateManager *statemanager.Manager) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m.ctx = nil
|
m.ctx = nil
|
||||||
|
|
||||||
|
m.mux.Lock()
|
||||||
|
defer m.mux.Unlock()
|
||||||
|
m.clientRoutes = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRoutes compares received routes with existing routes and removes, updates or adds them to the client and server maps
|
// UpdateRoutes compares received routes with existing routes and removes, updates or adds them to the client and server maps
|
||||||
func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) (map[route.ID]*route.Route, route.HAMap, error) {
|
func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route, useNewDNSRoute bool) error {
|
||||||
select {
|
select {
|
||||||
case <-m.ctx.Done():
|
case <-m.ctx.Done():
|
||||||
log.Infof("not updating routes as context is closed")
|
log.Infof("not updating routes as context is closed")
|
||||||
return nil, nil, m.ctx.Err()
|
return nil
|
||||||
default:
|
default:
|
||||||
m.mux.Lock()
|
|
||||||
defer m.mux.Unlock()
|
|
||||||
|
|
||||||
newServerRoutesMap, newClientRoutesIDMap := m.classifyRoutes(newRoutes)
|
|
||||||
|
|
||||||
filteredClientRoutes := m.routeSelector.FilterSelected(newClientRoutesIDMap)
|
|
||||||
m.updateClientNetworks(updateSerial, filteredClientRoutes)
|
|
||||||
m.notifier.OnNewRoutes(filteredClientRoutes)
|
|
||||||
|
|
||||||
if m.serverRouter != nil {
|
|
||||||
err := m.serverRouter.updateRoutes(newServerRoutesMap)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("update routes: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newServerRoutesMap, newClientRoutesIDMap, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.mux.Lock()
|
||||||
|
defer m.mux.Unlock()
|
||||||
|
m.useNewDNSRoute = useNewDNSRoute
|
||||||
|
|
||||||
|
newServerRoutesMap, newClientRoutesIDMap := m.classifyRoutes(newRoutes)
|
||||||
|
|
||||||
|
filteredClientRoutes := m.routeSelector.FilterSelected(newClientRoutesIDMap)
|
||||||
|
m.updateClientNetworks(updateSerial, filteredClientRoutes)
|
||||||
|
m.notifier.OnNewRoutes(filteredClientRoutes)
|
||||||
|
|
||||||
|
if m.serverRouter != nil {
|
||||||
|
err := m.serverRouter.updateRoutes(newServerRoutesMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.clientRoutes = newClientRoutesIDMap
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRouteChangeListener set RouteListener for route change Notifier
|
// SetRouteChangeListener set RouteListener for route change Notifier
|
||||||
@ -251,9 +273,24 @@ func (m *DefaultManager) GetRouteSelector() *routeselector.RouteSelector {
|
|||||||
return m.routeSelector
|
return m.routeSelector
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClientRoutes returns the client routes
|
// GetClientRoutes returns most recent list of clientRoutes received from the Management Service
|
||||||
func (m *DefaultManager) GetClientRoutes() map[route.HAUniqueID]*clientNetwork {
|
func (m *DefaultManager) GetClientRoutes() route.HAMap {
|
||||||
return m.clientNetworks
|
m.mux.Lock()
|
||||||
|
defer m.mux.Unlock()
|
||||||
|
|
||||||
|
return maps.Clone(m.clientRoutes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClientRoutesWithNetID returns the current routes from the route map, but the keys consist of the network ID only
|
||||||
|
func (m *DefaultManager) GetClientRoutesWithNetID() map[route.NetID][]*route.Route {
|
||||||
|
m.mux.Lock()
|
||||||
|
defer m.mux.Unlock()
|
||||||
|
|
||||||
|
routes := make(map[route.NetID][]*route.Route, len(m.clientRoutes))
|
||||||
|
for id, v := range m.clientRoutes {
|
||||||
|
routes[id.NetID()] = v
|
||||||
|
}
|
||||||
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
// TriggerSelection triggers the selection of routes, stopping deselected watchers and starting newly selected ones
|
// TriggerSelection triggers the selection of routes, stopping deselected watchers and starting newly selected ones
|
||||||
@ -273,7 +310,18 @@ func (m *DefaultManager) TriggerSelection(networks route.HAMap) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
clientNetworkWatcher := newClientNetworkWatcher(m.ctx, m.dnsRouteInterval, m.wgInterface, m.statusRecorder, routes[0], m.routeRefCounter, m.allowedIPsRefCounter)
|
clientNetworkWatcher := newClientNetworkWatcher(
|
||||||
|
m.ctx,
|
||||||
|
m.dnsRouteInterval,
|
||||||
|
m.wgInterface,
|
||||||
|
m.statusRecorder,
|
||||||
|
routes[0],
|
||||||
|
m.routeRefCounter,
|
||||||
|
m.allowedIPsRefCounter,
|
||||||
|
m.dnsServer,
|
||||||
|
m.peerStore,
|
||||||
|
m.useNewDNSRoute,
|
||||||
|
)
|
||||||
m.clientNetworks[id] = clientNetworkWatcher
|
m.clientNetworks[id] = clientNetworkWatcher
|
||||||
go clientNetworkWatcher.peersStateAndUpdateWatcher()
|
go clientNetworkWatcher.peersStateAndUpdateWatcher()
|
||||||
clientNetworkWatcher.sendUpdateToClientNetworkWatcher(routesUpdate{routes: routes})
|
clientNetworkWatcher.sendUpdateToClientNetworkWatcher(routesUpdate{routes: routes})
|
||||||
@ -302,7 +350,18 @@ func (m *DefaultManager) updateClientNetworks(updateSerial uint64, networks rout
|
|||||||
for id, routes := range networks {
|
for id, routes := range networks {
|
||||||
clientNetworkWatcher, found := m.clientNetworks[id]
|
clientNetworkWatcher, found := m.clientNetworks[id]
|
||||||
if !found {
|
if !found {
|
||||||
clientNetworkWatcher = newClientNetworkWatcher(m.ctx, m.dnsRouteInterval, m.wgInterface, m.statusRecorder, routes[0], m.routeRefCounter, m.allowedIPsRefCounter)
|
clientNetworkWatcher = newClientNetworkWatcher(
|
||||||
|
m.ctx,
|
||||||
|
m.dnsRouteInterval,
|
||||||
|
m.wgInterface,
|
||||||
|
m.statusRecorder,
|
||||||
|
routes[0],
|
||||||
|
m.routeRefCounter,
|
||||||
|
m.allowedIPsRefCounter,
|
||||||
|
m.dnsServer,
|
||||||
|
m.peerStore,
|
||||||
|
m.useNewDNSRoute,
|
||||||
|
)
|
||||||
m.clientNetworks[id] = clientNetworkWatcher
|
m.clientNetworks[id] = clientNetworkWatcher
|
||||||
go clientNetworkWatcher.peersStateAndUpdateWatcher()
|
go clientNetworkWatcher.peersStateAndUpdateWatcher()
|
||||||
}
|
}
|
||||||
@ -345,7 +404,7 @@ func (m *DefaultManager) classifyRoutes(newRoutes []*route.Route) (map[route.ID]
|
|||||||
return newServerRoutesMap, newClientRoutesIDMap
|
return newServerRoutesMap, newClientRoutesIDMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DefaultManager) clientRoutes(initialRoutes []*route.Route) []*route.Route {
|
func (m *DefaultManager) initialClientRoutes(initialRoutes []*route.Route) []*route.Route {
|
||||||
_, crMap := m.classifyRoutes(initialRoutes)
|
_, crMap := m.classifyRoutes(initialRoutes)
|
||||||
rs := make([]*route.Route, 0, len(crMap))
|
rs := make([]*route.Route, 0, len(crMap))
|
||||||
for _, routes := range crMap {
|
for _, routes := range crMap {
|
||||||
|
@ -424,7 +424,7 @@ func TestManagerUpdateRoutes(t *testing.T) {
|
|||||||
|
|
||||||
statusRecorder := peer.NewRecorder("https://mgm")
|
statusRecorder := peer.NewRecorder("https://mgm")
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
routeManager := NewManager(ctx, localPeerKey, 0, wgInterface, statusRecorder, nil, nil, nil)
|
routeManager := NewManager(ctx, localPeerKey, 0, wgInterface, statusRecorder, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
_, _, err = routeManager.Init()
|
_, _, err = routeManager.Init()
|
||||||
|
|
||||||
@ -436,11 +436,11 @@ func TestManagerUpdateRoutes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(testCase.inputInitRoutes) > 0 {
|
if len(testCase.inputInitRoutes) > 0 {
|
||||||
_, _, err = routeManager.UpdateRoutes(testCase.inputSerial, testCase.inputRoutes)
|
_ = routeManager.UpdateRoutes(testCase.inputSerial, testCase.inputRoutes, false)
|
||||||
require.NoError(t, err, "should update routes with init routes")
|
require.NoError(t, err, "should update routes with init routes")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, err = routeManager.UpdateRoutes(testCase.inputSerial+uint64(len(testCase.inputInitRoutes)), testCase.inputRoutes)
|
_ = routeManager.UpdateRoutes(testCase.inputSerial+uint64(len(testCase.inputInitRoutes)), testCase.inputRoutes, false)
|
||||||
require.NoError(t, err, "should update routes")
|
require.NoError(t, err, "should update routes")
|
||||||
|
|
||||||
expectedWatchers := testCase.clientNetworkWatchersExpected
|
expectedWatchers := testCase.clientNetworkWatchersExpected
|
||||||
|
@ -2,7 +2,6 @@ package routemanager
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
firewall "github.com/netbirdio/netbird/client/firewall/manager"
|
firewall "github.com/netbirdio/netbird/client/firewall/manager"
|
||||||
"github.com/netbirdio/netbird/client/iface"
|
"github.com/netbirdio/netbird/client/iface"
|
||||||
@ -15,10 +14,12 @@ import (
|
|||||||
|
|
||||||
// MockManager is the mock instance of a route manager
|
// MockManager is the mock instance of a route manager
|
||||||
type MockManager struct {
|
type MockManager struct {
|
||||||
UpdateRoutesFunc func(updateSerial uint64, newRoutes []*route.Route) (map[route.ID]*route.Route, route.HAMap, error)
|
UpdateRoutesFunc func(updateSerial uint64, newRoutes []*route.Route) error
|
||||||
TriggerSelectionFunc func(haMap route.HAMap)
|
TriggerSelectionFunc func(haMap route.HAMap)
|
||||||
GetRouteSelectorFunc func() *routeselector.RouteSelector
|
GetRouteSelectorFunc func() *routeselector.RouteSelector
|
||||||
StopFunc func(manager *statemanager.Manager)
|
GetClientRoutesFunc func() route.HAMap
|
||||||
|
GetClientRoutesWithNetIDFunc func() map[route.NetID][]*route.Route
|
||||||
|
StopFunc func(manager *statemanager.Manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockManager) Init() (net.AddHookFunc, net.RemoveHookFunc, error) {
|
func (m *MockManager) Init() (net.AddHookFunc, net.RemoveHookFunc, error) {
|
||||||
@ -31,11 +32,11 @@ func (m *MockManager) InitialRouteRange() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRoutes mock implementation of UpdateRoutes from Manager interface
|
// UpdateRoutes mock implementation of UpdateRoutes from Manager interface
|
||||||
func (m *MockManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) (map[route.ID]*route.Route, route.HAMap, error) {
|
func (m *MockManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route, b bool) error {
|
||||||
if m.UpdateRoutesFunc != nil {
|
if m.UpdateRoutesFunc != nil {
|
||||||
return m.UpdateRoutesFunc(updateSerial, newRoutes)
|
return m.UpdateRoutesFunc(updateSerial, newRoutes)
|
||||||
}
|
}
|
||||||
return nil, nil, fmt.Errorf("method UpdateRoutes is not implemented")
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockManager) TriggerSelection(networks route.HAMap) {
|
func (m *MockManager) TriggerSelection(networks route.HAMap) {
|
||||||
@ -52,6 +53,22 @@ func (m *MockManager) GetRouteSelector() *routeselector.RouteSelector {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetClientRoutes mock implementation of GetClientRoutes from Manager interface
|
||||||
|
func (m *MockManager) GetClientRoutes() route.HAMap {
|
||||||
|
if m.GetClientRoutesFunc != nil {
|
||||||
|
return m.GetClientRoutesFunc()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClientRoutesWithNetID mock implementation of GetClientRoutesWithNetID from Manager interface
|
||||||
|
func (m *MockManager) GetClientRoutesWithNetID() map[route.NetID][]*route.Route {
|
||||||
|
if m.GetClientRoutesWithNetIDFunc != nil {
|
||||||
|
return m.GetClientRoutesWithNetIDFunc()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Start mock implementation of Start from Manager interface
|
// Start mock implementation of Start from Manager interface
|
||||||
func (m *MockManager) Start(ctx context.Context, iface *iface.WGIface) {
|
func (m *MockManager) Start(ctx context.Context, iface *iface.WGIface) {
|
||||||
}
|
}
|
||||||
|
@ -272,8 +272,8 @@ func (c *Client) GetRoutesSelectionDetails() (*RoutesSelectionDetails, error) {
|
|||||||
return nil, fmt.Errorf("not connected")
|
return nil, fmt.Errorf("not connected")
|
||||||
}
|
}
|
||||||
|
|
||||||
routesMap := engine.GetClientRoutesWithNetID()
|
|
||||||
routeManager := engine.GetRouteManager()
|
routeManager := engine.GetRouteManager()
|
||||||
|
routesMap := routeManager.GetClientRoutesWithNetID()
|
||||||
if routeManager == nil {
|
if routeManager == nil {
|
||||||
return nil, fmt.Errorf("could not get route manager")
|
return nil, fmt.Errorf("could not get route manager")
|
||||||
}
|
}
|
||||||
@ -317,7 +317,7 @@ func (c *Client) GetRoutesSelectionDetails() (*RoutesSelectionDetails, error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareRouteSelectionDetails(routes []*selectRoute, resolvedDomains map[domain.Domain][]netip.Prefix) *RoutesSelectionDetails {
|
func prepareRouteSelectionDetails(routes []*selectRoute, resolvedDomains map[domain.Domain]peer.ResolvedDomainInfo) *RoutesSelectionDetails {
|
||||||
var routeSelection []RoutesSelectionInfo
|
var routeSelection []RoutesSelectionInfo
|
||||||
for _, r := range routes {
|
for _, r := range routes {
|
||||||
domainList := make([]DomainInfo, 0)
|
domainList := make([]DomainInfo, 0)
|
||||||
@ -325,9 +325,10 @@ func prepareRouteSelectionDetails(routes []*selectRoute, resolvedDomains map[dom
|
|||||||
domainResp := DomainInfo{
|
domainResp := DomainInfo{
|
||||||
Domain: d.SafeString(),
|
Domain: d.SafeString(),
|
||||||
}
|
}
|
||||||
if prefixes, exists := resolvedDomains[d]; exists {
|
|
||||||
|
if info, exists := resolvedDomains[d]; exists {
|
||||||
var ipStrings []string
|
var ipStrings []string
|
||||||
for _, prefix := range prefixes {
|
for _, prefix := range info.Prefixes {
|
||||||
ipStrings = append(ipStrings, prefix.Addr().String())
|
ipStrings = append(ipStrings, prefix.Addr().String())
|
||||||
}
|
}
|
||||||
domainResp.ResolvedIPs = strings.Join(ipStrings, ", ")
|
domainResp.ResolvedIPs = strings.Join(ipStrings, ", ")
|
||||||
@ -365,12 +366,12 @@ func (c *Client) SelectRoute(id string) error {
|
|||||||
} else {
|
} else {
|
||||||
log.Debugf("select route with id: %s", id)
|
log.Debugf("select route with id: %s", id)
|
||||||
routes := toNetIDs([]string{id})
|
routes := toNetIDs([]string{id})
|
||||||
if err := routeSelector.SelectRoutes(routes, true, maps.Keys(engine.GetClientRoutesWithNetID())); err != nil {
|
if err := routeSelector.SelectRoutes(routes, true, maps.Keys(routeManager.GetClientRoutesWithNetID())); err != nil {
|
||||||
log.Debugf("error when selecting routes: %s", err)
|
log.Debugf("error when selecting routes: %s", err)
|
||||||
return fmt.Errorf("select routes: %w", err)
|
return fmt.Errorf("select routes: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
routeManager.TriggerSelection(engine.GetClientRoutes())
|
routeManager.TriggerSelection(routeManager.GetClientRoutes())
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -392,12 +393,12 @@ func (c *Client) DeselectRoute(id string) error {
|
|||||||
} else {
|
} else {
|
||||||
log.Debugf("deselect route with id: %s", id)
|
log.Debugf("deselect route with id: %s", id)
|
||||||
routes := toNetIDs([]string{id})
|
routes := toNetIDs([]string{id})
|
||||||
if err := routeSelector.DeselectRoutes(routes, maps.Keys(engine.GetClientRoutesWithNetID())); err != nil {
|
if err := routeSelector.DeselectRoutes(routes, maps.Keys(routeManager.GetClientRoutesWithNetID())); err != nil {
|
||||||
log.Debugf("error when deselecting routes: %s", err)
|
log.Debugf("error when deselecting routes: %s", err)
|
||||||
return fmt.Errorf("deselect routes: %w", err)
|
return fmt.Errorf("deselect routes: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
routeManager.TriggerSelection(engine.GetClientRoutes())
|
routeManager.TriggerSelection(routeManager.GetClientRoutes())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.26.0
|
// protoc-gen-go v1.26.0
|
||||||
// protoc v4.23.4
|
// protoc v3.21.9
|
||||||
// source: daemon.proto
|
// source: daemon.proto
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
@ -908,7 +908,7 @@ type PeerState struct {
|
|||||||
BytesRx int64 `protobuf:"varint,13,opt,name=bytesRx,proto3" json:"bytesRx,omitempty"`
|
BytesRx int64 `protobuf:"varint,13,opt,name=bytesRx,proto3" json:"bytesRx,omitempty"`
|
||||||
BytesTx int64 `protobuf:"varint,14,opt,name=bytesTx,proto3" json:"bytesTx,omitempty"`
|
BytesTx int64 `protobuf:"varint,14,opt,name=bytesTx,proto3" json:"bytesTx,omitempty"`
|
||||||
RosenpassEnabled bool `protobuf:"varint,15,opt,name=rosenpassEnabled,proto3" json:"rosenpassEnabled,omitempty"`
|
RosenpassEnabled bool `protobuf:"varint,15,opt,name=rosenpassEnabled,proto3" json:"rosenpassEnabled,omitempty"`
|
||||||
Routes []string `protobuf:"bytes,16,rep,name=routes,proto3" json:"routes,omitempty"`
|
Networks []string `protobuf:"bytes,16,rep,name=networks,proto3" json:"networks,omitempty"`
|
||||||
Latency *durationpb.Duration `protobuf:"bytes,17,opt,name=latency,proto3" json:"latency,omitempty"`
|
Latency *durationpb.Duration `protobuf:"bytes,17,opt,name=latency,proto3" json:"latency,omitempty"`
|
||||||
RelayAddress string `protobuf:"bytes,18,opt,name=relayAddress,proto3" json:"relayAddress,omitempty"`
|
RelayAddress string `protobuf:"bytes,18,opt,name=relayAddress,proto3" json:"relayAddress,omitempty"`
|
||||||
}
|
}
|
||||||
@ -1043,9 +1043,9 @@ func (x *PeerState) GetRosenpassEnabled() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PeerState) GetRoutes() []string {
|
func (x *PeerState) GetNetworks() []string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Routes
|
return x.Networks
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1076,7 +1076,7 @@ type LocalPeerState struct {
|
|||||||
Fqdn string `protobuf:"bytes,4,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
|
Fqdn string `protobuf:"bytes,4,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
|
||||||
RosenpassEnabled bool `protobuf:"varint,5,opt,name=rosenpassEnabled,proto3" json:"rosenpassEnabled,omitempty"`
|
RosenpassEnabled bool `protobuf:"varint,5,opt,name=rosenpassEnabled,proto3" json:"rosenpassEnabled,omitempty"`
|
||||||
RosenpassPermissive bool `protobuf:"varint,6,opt,name=rosenpassPermissive,proto3" json:"rosenpassPermissive,omitempty"`
|
RosenpassPermissive bool `protobuf:"varint,6,opt,name=rosenpassPermissive,proto3" json:"rosenpassPermissive,omitempty"`
|
||||||
Routes []string `protobuf:"bytes,7,rep,name=routes,proto3" json:"routes,omitempty"`
|
Networks []string `protobuf:"bytes,7,rep,name=networks,proto3" json:"networks,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *LocalPeerState) Reset() {
|
func (x *LocalPeerState) Reset() {
|
||||||
@ -1153,9 +1153,9 @@ func (x *LocalPeerState) GetRosenpassPermissive() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *LocalPeerState) GetRoutes() []string {
|
func (x *LocalPeerState) GetNetworks() []string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Routes
|
return x.Networks
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1511,14 +1511,14 @@ func (x *FullStatus) GetDnsServers() []*NSGroupState {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListRoutesRequest struct {
|
type ListNetworksRequest struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ListRoutesRequest) Reset() {
|
func (x *ListNetworksRequest) Reset() {
|
||||||
*x = ListRoutesRequest{}
|
*x = ListNetworksRequest{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_daemon_proto_msgTypes[19]
|
mi := &file_daemon_proto_msgTypes[19]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
@ -1526,13 +1526,13 @@ func (x *ListRoutesRequest) Reset() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ListRoutesRequest) String() string {
|
func (x *ListNetworksRequest) String() string {
|
||||||
return protoimpl.X.MessageStringOf(x)
|
return protoimpl.X.MessageStringOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*ListRoutesRequest) ProtoMessage() {}
|
func (*ListNetworksRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *ListRoutesRequest) ProtoReflect() protoreflect.Message {
|
func (x *ListNetworksRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_daemon_proto_msgTypes[19]
|
mi := &file_daemon_proto_msgTypes[19]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
@ -1544,21 +1544,21 @@ func (x *ListRoutesRequest) ProtoReflect() protoreflect.Message {
|
|||||||
return mi.MessageOf(x)
|
return mi.MessageOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Use ListRoutesRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use ListNetworksRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*ListRoutesRequest) Descriptor() ([]byte, []int) {
|
func (*ListNetworksRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_daemon_proto_rawDescGZIP(), []int{19}
|
return file_daemon_proto_rawDescGZIP(), []int{19}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListRoutesResponse struct {
|
type ListNetworksResponse struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
Routes []*Route `protobuf:"bytes,1,rep,name=routes,proto3" json:"routes,omitempty"`
|
Routes []*Network `protobuf:"bytes,1,rep,name=routes,proto3" json:"routes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ListRoutesResponse) Reset() {
|
func (x *ListNetworksResponse) Reset() {
|
||||||
*x = ListRoutesResponse{}
|
*x = ListNetworksResponse{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_daemon_proto_msgTypes[20]
|
mi := &file_daemon_proto_msgTypes[20]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
@ -1566,13 +1566,13 @@ func (x *ListRoutesResponse) Reset() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ListRoutesResponse) String() string {
|
func (x *ListNetworksResponse) String() string {
|
||||||
return protoimpl.X.MessageStringOf(x)
|
return protoimpl.X.MessageStringOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*ListRoutesResponse) ProtoMessage() {}
|
func (*ListNetworksResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *ListRoutesResponse) ProtoReflect() protoreflect.Message {
|
func (x *ListNetworksResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_daemon_proto_msgTypes[20]
|
mi := &file_daemon_proto_msgTypes[20]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
@ -1584,30 +1584,30 @@ func (x *ListRoutesResponse) ProtoReflect() protoreflect.Message {
|
|||||||
return mi.MessageOf(x)
|
return mi.MessageOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Use ListRoutesResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use ListNetworksResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*ListRoutesResponse) Descriptor() ([]byte, []int) {
|
func (*ListNetworksResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_daemon_proto_rawDescGZIP(), []int{20}
|
return file_daemon_proto_rawDescGZIP(), []int{20}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ListRoutesResponse) GetRoutes() []*Route {
|
func (x *ListNetworksResponse) GetRoutes() []*Network {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Routes
|
return x.Routes
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type SelectRoutesRequest struct {
|
type SelectNetworksRequest struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
RouteIDs []string `protobuf:"bytes,1,rep,name=routeIDs,proto3" json:"routeIDs,omitempty"`
|
NetworkIDs []string `protobuf:"bytes,1,rep,name=networkIDs,proto3" json:"networkIDs,omitempty"`
|
||||||
Append bool `protobuf:"varint,2,opt,name=append,proto3" json:"append,omitempty"`
|
Append bool `protobuf:"varint,2,opt,name=append,proto3" json:"append,omitempty"`
|
||||||
All bool `protobuf:"varint,3,opt,name=all,proto3" json:"all,omitempty"`
|
All bool `protobuf:"varint,3,opt,name=all,proto3" json:"all,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SelectRoutesRequest) Reset() {
|
func (x *SelectNetworksRequest) Reset() {
|
||||||
*x = SelectRoutesRequest{}
|
*x = SelectNetworksRequest{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_daemon_proto_msgTypes[21]
|
mi := &file_daemon_proto_msgTypes[21]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
@ -1615,13 +1615,13 @@ func (x *SelectRoutesRequest) Reset() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SelectRoutesRequest) String() string {
|
func (x *SelectNetworksRequest) String() string {
|
||||||
return protoimpl.X.MessageStringOf(x)
|
return protoimpl.X.MessageStringOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*SelectRoutesRequest) ProtoMessage() {}
|
func (*SelectNetworksRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *SelectRoutesRequest) ProtoReflect() protoreflect.Message {
|
func (x *SelectNetworksRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_daemon_proto_msgTypes[21]
|
mi := &file_daemon_proto_msgTypes[21]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
@ -1633,40 +1633,40 @@ func (x *SelectRoutesRequest) ProtoReflect() protoreflect.Message {
|
|||||||
return mi.MessageOf(x)
|
return mi.MessageOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Use SelectRoutesRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use SelectNetworksRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*SelectRoutesRequest) Descriptor() ([]byte, []int) {
|
func (*SelectNetworksRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_daemon_proto_rawDescGZIP(), []int{21}
|
return file_daemon_proto_rawDescGZIP(), []int{21}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SelectRoutesRequest) GetRouteIDs() []string {
|
func (x *SelectNetworksRequest) GetNetworkIDs() []string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.RouteIDs
|
return x.NetworkIDs
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SelectRoutesRequest) GetAppend() bool {
|
func (x *SelectNetworksRequest) GetAppend() bool {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Append
|
return x.Append
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SelectRoutesRequest) GetAll() bool {
|
func (x *SelectNetworksRequest) GetAll() bool {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.All
|
return x.All
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
type SelectRoutesResponse struct {
|
type SelectNetworksResponse struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SelectRoutesResponse) Reset() {
|
func (x *SelectNetworksResponse) Reset() {
|
||||||
*x = SelectRoutesResponse{}
|
*x = SelectNetworksResponse{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_daemon_proto_msgTypes[22]
|
mi := &file_daemon_proto_msgTypes[22]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
@ -1674,13 +1674,13 @@ func (x *SelectRoutesResponse) Reset() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SelectRoutesResponse) String() string {
|
func (x *SelectNetworksResponse) String() string {
|
||||||
return protoimpl.X.MessageStringOf(x)
|
return protoimpl.X.MessageStringOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*SelectRoutesResponse) ProtoMessage() {}
|
func (*SelectNetworksResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *SelectRoutesResponse) ProtoReflect() protoreflect.Message {
|
func (x *SelectNetworksResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_daemon_proto_msgTypes[22]
|
mi := &file_daemon_proto_msgTypes[22]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
@ -1692,8 +1692,8 @@ func (x *SelectRoutesResponse) ProtoReflect() protoreflect.Message {
|
|||||||
return mi.MessageOf(x)
|
return mi.MessageOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Use SelectRoutesResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use SelectNetworksResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*SelectRoutesResponse) Descriptor() ([]byte, []int) {
|
func (*SelectNetworksResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_daemon_proto_rawDescGZIP(), []int{22}
|
return file_daemon_proto_rawDescGZIP(), []int{22}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1744,20 +1744,20 @@ func (x *IPList) GetIps() []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Route struct {
|
type Network struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||||
Network string `protobuf:"bytes,2,opt,name=network,proto3" json:"network,omitempty"`
|
Range string `protobuf:"bytes,2,opt,name=range,proto3" json:"range,omitempty"`
|
||||||
Selected bool `protobuf:"varint,3,opt,name=selected,proto3" json:"selected,omitempty"`
|
Selected bool `protobuf:"varint,3,opt,name=selected,proto3" json:"selected,omitempty"`
|
||||||
Domains []string `protobuf:"bytes,4,rep,name=domains,proto3" json:"domains,omitempty"`
|
Domains []string `protobuf:"bytes,4,rep,name=domains,proto3" json:"domains,omitempty"`
|
||||||
ResolvedIPs map[string]*IPList `protobuf:"bytes,5,rep,name=resolvedIPs,proto3" json:"resolvedIPs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
ResolvedIPs map[string]*IPList `protobuf:"bytes,5,rep,name=resolvedIPs,proto3" json:"resolvedIPs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Route) Reset() {
|
func (x *Network) Reset() {
|
||||||
*x = Route{}
|
*x = Network{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_daemon_proto_msgTypes[24]
|
mi := &file_daemon_proto_msgTypes[24]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
@ -1765,13 +1765,13 @@ func (x *Route) Reset() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Route) String() string {
|
func (x *Network) String() string {
|
||||||
return protoimpl.X.MessageStringOf(x)
|
return protoimpl.X.MessageStringOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Route) ProtoMessage() {}
|
func (*Network) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Route) ProtoReflect() protoreflect.Message {
|
func (x *Network) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_daemon_proto_msgTypes[24]
|
mi := &file_daemon_proto_msgTypes[24]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
@ -1783,40 +1783,40 @@ func (x *Route) ProtoReflect() protoreflect.Message {
|
|||||||
return mi.MessageOf(x)
|
return mi.MessageOf(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: Use Route.ProtoReflect.Descriptor instead.
|
// Deprecated: Use Network.ProtoReflect.Descriptor instead.
|
||||||
func (*Route) Descriptor() ([]byte, []int) {
|
func (*Network) Descriptor() ([]byte, []int) {
|
||||||
return file_daemon_proto_rawDescGZIP(), []int{24}
|
return file_daemon_proto_rawDescGZIP(), []int{24}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Route) GetID() string {
|
func (x *Network) GetID() string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.ID
|
return x.ID
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Route) GetNetwork() string {
|
func (x *Network) GetRange() string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Network
|
return x.Range
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Route) GetSelected() bool {
|
func (x *Network) GetSelected() bool {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Selected
|
return x.Selected
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Route) GetDomains() []string {
|
func (x *Network) GetDomains() []string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Domains
|
return x.Domains
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Route) GetResolvedIPs() map[string]*IPList {
|
func (x *Network) GetResolvedIPs() map[string]*IPList {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.ResolvedIPs
|
return x.ResolvedIPs
|
||||||
}
|
}
|
||||||
@ -2671,7 +2671,7 @@ var file_daemon_proto_rawDesc = []byte{
|
|||||||
0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e,
|
0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e,
|
||||||
0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x18, 0x0c,
|
0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x18, 0x0c,
|
||||||
0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50,
|
0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50,
|
||||||
0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x22, 0xda, 0x05, 0x0a, 0x09, 0x50, 0x65,
|
0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x22, 0xde, 0x05, 0x0a, 0x09, 0x50, 0x65,
|
||||||
0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20,
|
0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65,
|
0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65,
|
||||||
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12,
|
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12,
|
||||||
@ -2710,233 +2710,235 @@ var file_daemon_proto_rawDesc = []byte{
|
|||||||
0x52, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x54, 0x78, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x6f, 0x73,
|
0x52, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x54, 0x78, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x6f, 0x73,
|
||||||
0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0f, 0x20,
|
0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0f, 0x20,
|
||||||
0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e,
|
0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e,
|
||||||
0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18,
|
0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
|
||||||
0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x33, 0x0a,
|
0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
|
||||||
0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19,
|
0x73, 0x12, 0x33, 0x0a, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x11, 0x20, 0x01,
|
||||||
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
|
0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e,
|
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x6c,
|
||||||
0x63, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65,
|
0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x41,
|
||||||
0x73, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x41,
|
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65,
|
||||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xec, 0x01, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x6c,
|
0x6c, 0x61, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xf0, 0x01, 0x0a, 0x0e, 0x4c,
|
||||||
0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18,
|
0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62,
|
0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a,
|
||||||
0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65,
|
0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70,
|
||||||
0x79, 0x12, 0x28, 0x0a, 0x0f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72,
|
0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49,
|
||||||
0x66, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6b, 0x65, 0x72, 0x6e,
|
0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f,
|
||||||
0x65, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66,
|
0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12,
|
||||||
0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x12,
|
0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66,
|
||||||
0x2a, 0x0a, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62,
|
0x71, 0x64, 0x6e, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73,
|
||||||
0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e,
|
0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72,
|
||||||
0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x72,
|
0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12,
|
||||||
0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69,
|
0x30, 0x0a, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d,
|
||||||
0x76, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70,
|
0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x72, 0x6f,
|
||||||
0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x12, 0x16, 0x0a,
|
0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76,
|
||||||
0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72,
|
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x07, 0x20,
|
||||||
0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x53, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53,
|
0x03, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x22, 0x53, 0x0a,
|
||||||
0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x0b, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03,
|
||||||
0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
|
0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c,
|
||||||
0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
|
0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20,
|
0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x57, 0x0a, 0x0f, 0x4d, 0x61,
|
0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72,
|
||||||
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a,
|
0x6f, 0x72, 0x22, 0x57, 0x0a, 0x0f, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
||||||
0x03, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12,
|
0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01,
|
0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
|
||||||
0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a,
|
0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e,
|
||||||
0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72,
|
0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03,
|
||||||
0x72, 0x6f, 0x72, 0x22, 0x52, 0x0a, 0x0a, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x52, 0x0a, 0x0a, 0x52,
|
||||||
0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x49, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
|
0x65, 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x49,
|
||||||
0x55, 0x52, 0x49, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x49, 0x12, 0x1c, 0x0a, 0x09, 0x61,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c,
|
0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09,
|
||||||
0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72,
|
||||||
0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x72, 0x0a, 0x0c, 0x4e, 0x53, 0x47, 0x72, 0x6f,
|
0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22,
|
||||||
0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65,
|
0x72, 0x0a, 0x0c, 0x4e, 0x53, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
|
||||||
0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
|
0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09,
|
||||||
0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03,
|
0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d,
|
||||||
0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65,
|
0x61, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61,
|
||||||
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e,
|
0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03,
|
||||||
0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04,
|
0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x14, 0x0a,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xd2, 0x02, 0x0a, 0x0a,
|
0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72,
|
||||||
0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x41, 0x0a, 0x0f, 0x6d, 0x61,
|
0x72, 0x6f, 0x72, 0x22, 0xd2, 0x02, 0x0a, 0x0a, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74,
|
||||||
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20,
|
0x75, 0x73, 0x12, 0x41, 0x0a, 0x0f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
||||||
0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x61, 0x6e,
|
0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x61,
|
||||||
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x6d, 0x61,
|
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53,
|
||||||
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x35, 0x0a,
|
0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
||||||
0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01,
|
0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53,
|
||||||
0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x69, 0x67, 0x6e,
|
0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x61, 0x65,
|
||||||
0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53,
|
0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52,
|
||||||
0x74, 0x61, 0x74, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65,
|
0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3e, 0x0a, 0x0e,
|
||||||
0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64,
|
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03,
|
||||||
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53,
|
0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f,
|
||||||
0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53,
|
0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x6c, 0x6f,
|
||||||
0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20,
|
0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x05,
|
||||||
0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x65, 0x65,
|
0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x61,
|
||||||
0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a,
|
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05,
|
||||||
0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e,
|
0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18,
|
||||||
0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74,
|
0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x52,
|
||||||
0x65, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x64, 0x6e, 0x73,
|
0x65, 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79,
|
||||||
0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14,
|
0x73, 0x12, 0x35, 0x0a, 0x0b, 0x64, 0x6e, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73,
|
||||||
0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4e, 0x53, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53,
|
0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
|
||||||
0x74, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x64, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73,
|
0x4e, 0x53, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x64, 0x6e,
|
||||||
0x22, 0x13, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65,
|
0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74,
|
||||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75,
|
0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22,
|
||||||
0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x72,
|
0x3f, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52,
|
||||||
0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x64, 0x61,
|
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65,
|
||||||
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74,
|
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e,
|
||||||
0x65, 0x73, 0x22, 0x5b, 0x0a, 0x13, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74,
|
0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73,
|
||||||
0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x6f, 0x75,
|
0x22, 0x61, 0x0a, 0x15, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
|
||||||
0x74, 0x65, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x75,
|
0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x65, 0x74,
|
||||||
0x74, 0x65, 0x49, 0x44, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x18,
|
0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6e,
|
||||||
0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x12, 0x10, 0x0a,
|
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x70, 0x70,
|
||||||
0x03, 0x61, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22,
|
0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x61, 0x70, 0x70, 0x65, 0x6e,
|
||||||
0x16, 0x0a, 0x14, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52,
|
0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03,
|
||||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x0a, 0x06, 0x49, 0x50, 0x4c, 0x69, 0x73,
|
0x61, 0x6c, 0x6c, 0x22, 0x18, 0x0a, 0x16, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74,
|
||||||
0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03,
|
0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x0a,
|
||||||
0x69, 0x70, 0x73, 0x22, 0xf9, 0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x0e, 0x0a,
|
0x06, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x70, 0x73, 0x18, 0x01,
|
||||||
0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a,
|
0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x70, 0x73, 0x22, 0xf9, 0x01, 0x0a, 0x07, 0x4e, 0x65,
|
||||||
0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||||
0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63,
|
0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02,
|
||||||
0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73,
|
||||||
0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x04,
|
0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73,
|
||||||
0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x40, 0x0a,
|
0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69,
|
||||||
0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, 0x73, 0x18, 0x05, 0x20, 0x03,
|
0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||||
0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x6f, 0x75, 0x74,
|
0x73, 0x12, 0x42, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, 0x73,
|
||||||
0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, 0x73, 0x45, 0x6e, 0x74,
|
0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
|
||||||
0x72, 0x79, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, 0x73, 0x1a,
|
0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64,
|
||||||
0x4e, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, 0x73, 0x45, 0x6e,
|
0x49, 0x50, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76,
|
||||||
0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
0x65, 0x64, 0x49, 0x50, 0x73, 0x1a, 0x4e, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65,
|
||||||
0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
|
0x64, 0x49, 0x50, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
|
||||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x49, 0x50,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76,
|
||||||
0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22,
|
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x64, 0x61, 0x65,
|
||||||
0x6a, 0x0a, 0x12, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65,
|
0x6d, 0x6f, 0x6e, 0x2e, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
|
||||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x69,
|
0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x6a, 0x0a, 0x12, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75,
|
||||||
0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d,
|
0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x61,
|
||||||
0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20,
|
0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73,
|
0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61,
|
||||||
0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
|
0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
|
||||||
0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x29, 0x0a, 0x13, 0x44,
|
0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x18,
|
||||||
0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66,
|
||||||
0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
0x6f, 0x22, 0x29, 0x0a, 0x13, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65,
|
||||||
0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67,
|
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68,
|
||||||
0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3d, 0x0a, 0x13,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x14, 0x0a, 0x12,
|
||||||
0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01,
|
0x73, 0x74, 0x22, 0x3d, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65,
|
||||||
0x28, 0x0e, 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c,
|
0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76,
|
||||||
0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x3c, 0x0a, 0x12, 0x53,
|
0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
|
||||||
0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65,
|
||||||
0x74, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
|
0x6c, 0x22, 0x3c, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c,
|
||||||
0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76,
|
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c,
|
||||||
0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x65, 0x74,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
|
||||||
0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22,
|
||||||
0x22, 0x1b, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
|
0x15, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65,
|
||||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x13, 0x0a,
|
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
|
||||||
0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
|
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
|
||||||
0x73, 0x74, 0x22, 0x3b, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73,
|
0x61, 0x6d, 0x65, 0x22, 0x13, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65,
|
||||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74,
|
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74,
|
||||||
0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
|
0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25,
|
||||||
0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22,
|
0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d,
|
||||||
0x44, 0x0a, 0x11, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71,
|
0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x73,
|
||||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61,
|
0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0x44, 0x0a, 0x11, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74,
|
||||||
0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x4e,
|
0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74,
|
||||||
0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
|
0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
|
||||||
0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x3b, 0x0a, 0x12, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74,
|
0x73, 0x74, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c,
|
||||||
0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63,
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x3b, 0x0a, 0x12, 0x43,
|
||||||
0x6c, 0x65, 0x61, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20,
|
0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||||
0x01, 0x28, 0x05, 0x52, 0x0d, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74,
|
0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61,
|
||||||
0x65, 0x73, 0x22, 0x45, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74,
|
0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x63, 0x6c, 0x65, 0x61, 0x6e,
|
||||||
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74,
|
0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0x45, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65,
|
||||||
0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74,
|
0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d,
|
||||||
0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02,
|
0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x3c, 0x0a, 0x13, 0x44, 0x65, 0x6c,
|
0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a,
|
||||||
0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22,
|
||||||
0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74,
|
0x3c, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65,
|
||||||
0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65,
|
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65,
|
||||||
0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0x3b, 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x4e, 0x65,
|
0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d,
|
||||||
0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65,
|
0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0x3b, 0x0a,
|
||||||
0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e,
|
0x1f, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65,
|
||||||
0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61,
|
0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
0x62, 0x6c, 0x65, 0x64, 0x22, 0x22, 0x0a, 0x20, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f,
|
0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||||
0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65,
|
0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x22, 0x0a, 0x20, 0x53, 0x65,
|
||||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x62, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c,
|
|
||||||
0x65, 0x76, 0x65, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10,
|
|
||||||
0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x4e, 0x49, 0x43, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05,
|
|
||||||
0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52,
|
|
||||||
0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04,
|
|
||||||
0x49, 0x4e, 0x46, 0x4f, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10,
|
|
||||||
0x06, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x07, 0x32, 0x81, 0x09, 0x0a,
|
|
||||||
0x0d, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x36,
|
|
||||||
0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e,
|
|
||||||
0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e,
|
|
||||||
0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70,
|
|
||||||
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53,
|
|
||||||
0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
|
|
||||||
0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75,
|
|
||||||
0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69,
|
|
||||||
0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
|
||||||
0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, 0x70, 0x12, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d,
|
|
||||||
0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64,
|
|
||||||
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
|
||||||
0x22, 0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x15, 0x2e, 0x64,
|
|
||||||
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75,
|
|
||||||
0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61,
|
|
||||||
0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a,
|
|
||||||
0x04, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44,
|
|
||||||
0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x64, 0x61, 0x65,
|
|
||||||
0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
|
||||||
0x22, 0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
|
|
||||||
0x18, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
|
||||||
0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d,
|
|
||||||
0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70,
|
|
||||||
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f,
|
|
||||||
0x75, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69,
|
|
||||||
0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
|
||||||
0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75,
|
|
||||||
0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a,
|
|
||||||
0x0c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e,
|
|
||||||
0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75,
|
|
||||||
0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65,
|
|
||||||
0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73,
|
|
||||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x65,
|
|
||||||
0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x64,
|
|
||||||
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74,
|
|
||||||
0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d,
|
|
||||||
0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52,
|
|
||||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x44, 0x65, 0x62,
|
|
||||||
0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
|
|
||||||
0x6e, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71,
|
|
||||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65,
|
|
||||||
0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
|
||||||
0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76,
|
|
||||||
0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c,
|
|
||||||
0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b,
|
|
||||||
0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65,
|
|
||||||
0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a,
|
|
||||||
0x0b, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64,
|
|
||||||
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65,
|
|
||||||
0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
|
|
||||||
0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73,
|
|
||||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53,
|
|
||||||
0x74, 0x61, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c,
|
|
||||||
0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
|
||||||
0x1a, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74,
|
|
||||||
0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45,
|
|
||||||
0x0a, 0x0a, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x64,
|
|
||||||
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65,
|
|
||||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e,
|
|
||||||
0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
|
||||||
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53,
|
|
||||||
0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65,
|
|
||||||
0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
|
||||||
0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
|
|
||||||
0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
|
|
||||||
0x6f, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70,
|
|
||||||
0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x27, 0x2e, 0x64, 0x61,
|
|
||||||
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d,
|
|
||||||
0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71,
|
|
||||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65,
|
|
||||||
0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69,
|
0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69,
|
||||||
0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
|
0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x62,
|
||||||
0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e,
|
||||||
0x6f, 0x33,
|
0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x4e, 0x49, 0x43,
|
||||||
|
0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x09, 0x0a,
|
||||||
|
0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e,
|
||||||
|
0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05,
|
||||||
|
0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45,
|
||||||
|
0x10, 0x07, 0x32, 0x93, 0x09, 0x0a, 0x0d, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72,
|
||||||
|
0x76, 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x2e,
|
||||||
|
0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75,
|
||||||
|
0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67,
|
||||||
|
0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c,
|
||||||
|
0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x2e, 0x64,
|
||||||
|
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67,
|
||||||
|
0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d,
|
||||||
|
0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52,
|
||||||
|
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, 0x70, 0x12,
|
||||||
|
0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
|
0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65,
|
||||||
|
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74,
|
||||||
|
0x75, 0x73, 0x12, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74,
|
||||||
|
0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d,
|
||||||
|
0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||||
|
0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x04, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, 0x64, 0x61,
|
||||||
|
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
|
0x1a, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65,
|
||||||
|
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43,
|
||||||
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47,
|
||||||
|
0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||||
|
0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
|
0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c,
|
||||||
|
0x4c, 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x1b, 0x2e, 0x64,
|
||||||
|
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
|
||||||
|
0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d,
|
||||||
|
0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52,
|
||||||
|
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0e, 0x53, 0x65, 0x6c,
|
||||||
|
0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x1d, 0x2e, 0x64, 0x61,
|
||||||
|
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f,
|
||||||
|
0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x64, 0x61, 0x65,
|
||||||
|
0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
|
||||||
|
0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x10,
|
||||||
|
0x44, 0x65, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73,
|
||||||
|
0x12, 0x1d, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
|
||||||
|
0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||||
|
0x1e, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e,
|
||||||
|
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||||
|
0x00, 0x12, 0x48, 0x0a, 0x0b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65,
|
||||||
|
0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42,
|
||||||
|
0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64,
|
||||||
|
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c,
|
||||||
|
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x47,
|
||||||
|
0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65,
|
||||||
|
0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52,
|
||||||
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
|
||||||
|
0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
|
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c,
|
||||||
|
0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65,
|
||||||
|
0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
|
0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67,
|
||||||
|
0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
|
||||||
|
0x45, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e,
|
||||||
|
0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65,
|
||||||
|
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
|
||||||
|
0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
|
||||||
|
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53,
|
||||||
|
0x74, 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6c,
|
||||||
|
0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||||
|
0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74,
|
||||||
|
0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a,
|
||||||
|
0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x64,
|
||||||
|
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74,
|
||||||
|
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
|
||||||
|
0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73,
|
||||||
|
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x4e, 0x65,
|
||||||
|
0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65,
|
||||||
|
0x6e, 0x63, 0x65, 0x12, 0x27, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74,
|
||||||
|
0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73,
|
||||||
|
0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64,
|
||||||
|
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
|
||||||
|
0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65,
|
||||||
|
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -2974,12 +2976,12 @@ var file_daemon_proto_goTypes = []interface{}{
|
|||||||
(*RelayState)(nil), // 17: daemon.RelayState
|
(*RelayState)(nil), // 17: daemon.RelayState
|
||||||
(*NSGroupState)(nil), // 18: daemon.NSGroupState
|
(*NSGroupState)(nil), // 18: daemon.NSGroupState
|
||||||
(*FullStatus)(nil), // 19: daemon.FullStatus
|
(*FullStatus)(nil), // 19: daemon.FullStatus
|
||||||
(*ListRoutesRequest)(nil), // 20: daemon.ListRoutesRequest
|
(*ListNetworksRequest)(nil), // 20: daemon.ListNetworksRequest
|
||||||
(*ListRoutesResponse)(nil), // 21: daemon.ListRoutesResponse
|
(*ListNetworksResponse)(nil), // 21: daemon.ListNetworksResponse
|
||||||
(*SelectRoutesRequest)(nil), // 22: daemon.SelectRoutesRequest
|
(*SelectNetworksRequest)(nil), // 22: daemon.SelectNetworksRequest
|
||||||
(*SelectRoutesResponse)(nil), // 23: daemon.SelectRoutesResponse
|
(*SelectNetworksResponse)(nil), // 23: daemon.SelectNetworksResponse
|
||||||
(*IPList)(nil), // 24: daemon.IPList
|
(*IPList)(nil), // 24: daemon.IPList
|
||||||
(*Route)(nil), // 25: daemon.Route
|
(*Network)(nil), // 25: daemon.Network
|
||||||
(*DebugBundleRequest)(nil), // 26: daemon.DebugBundleRequest
|
(*DebugBundleRequest)(nil), // 26: daemon.DebugBundleRequest
|
||||||
(*DebugBundleResponse)(nil), // 27: daemon.DebugBundleResponse
|
(*DebugBundleResponse)(nil), // 27: daemon.DebugBundleResponse
|
||||||
(*GetLogLevelRequest)(nil), // 28: daemon.GetLogLevelRequest
|
(*GetLogLevelRequest)(nil), // 28: daemon.GetLogLevelRequest
|
||||||
@ -2995,7 +2997,7 @@ var file_daemon_proto_goTypes = []interface{}{
|
|||||||
(*DeleteStateResponse)(nil), // 38: daemon.DeleteStateResponse
|
(*DeleteStateResponse)(nil), // 38: daemon.DeleteStateResponse
|
||||||
(*SetNetworkMapPersistenceRequest)(nil), // 39: daemon.SetNetworkMapPersistenceRequest
|
(*SetNetworkMapPersistenceRequest)(nil), // 39: daemon.SetNetworkMapPersistenceRequest
|
||||||
(*SetNetworkMapPersistenceResponse)(nil), // 40: daemon.SetNetworkMapPersistenceResponse
|
(*SetNetworkMapPersistenceResponse)(nil), // 40: daemon.SetNetworkMapPersistenceResponse
|
||||||
nil, // 41: daemon.Route.ResolvedIPsEntry
|
nil, // 41: daemon.Network.ResolvedIPsEntry
|
||||||
(*durationpb.Duration)(nil), // 42: google.protobuf.Duration
|
(*durationpb.Duration)(nil), // 42: google.protobuf.Duration
|
||||||
(*timestamppb.Timestamp)(nil), // 43: google.protobuf.Timestamp
|
(*timestamppb.Timestamp)(nil), // 43: google.protobuf.Timestamp
|
||||||
}
|
}
|
||||||
@ -3011,21 +3013,21 @@ var file_daemon_proto_depIdxs = []int32{
|
|||||||
13, // 8: daemon.FullStatus.peers:type_name -> daemon.PeerState
|
13, // 8: daemon.FullStatus.peers:type_name -> daemon.PeerState
|
||||||
17, // 9: daemon.FullStatus.relays:type_name -> daemon.RelayState
|
17, // 9: daemon.FullStatus.relays:type_name -> daemon.RelayState
|
||||||
18, // 10: daemon.FullStatus.dns_servers:type_name -> daemon.NSGroupState
|
18, // 10: daemon.FullStatus.dns_servers:type_name -> daemon.NSGroupState
|
||||||
25, // 11: daemon.ListRoutesResponse.routes:type_name -> daemon.Route
|
25, // 11: daemon.ListNetworksResponse.routes:type_name -> daemon.Network
|
||||||
41, // 12: daemon.Route.resolvedIPs:type_name -> daemon.Route.ResolvedIPsEntry
|
41, // 12: daemon.Network.resolvedIPs:type_name -> daemon.Network.ResolvedIPsEntry
|
||||||
0, // 13: daemon.GetLogLevelResponse.level:type_name -> daemon.LogLevel
|
0, // 13: daemon.GetLogLevelResponse.level:type_name -> daemon.LogLevel
|
||||||
0, // 14: daemon.SetLogLevelRequest.level:type_name -> daemon.LogLevel
|
0, // 14: daemon.SetLogLevelRequest.level:type_name -> daemon.LogLevel
|
||||||
32, // 15: daemon.ListStatesResponse.states:type_name -> daemon.State
|
32, // 15: daemon.ListStatesResponse.states:type_name -> daemon.State
|
||||||
24, // 16: daemon.Route.ResolvedIPsEntry.value:type_name -> daemon.IPList
|
24, // 16: daemon.Network.ResolvedIPsEntry.value:type_name -> daemon.IPList
|
||||||
1, // 17: daemon.DaemonService.Login:input_type -> daemon.LoginRequest
|
1, // 17: daemon.DaemonService.Login:input_type -> daemon.LoginRequest
|
||||||
3, // 18: daemon.DaemonService.WaitSSOLogin:input_type -> daemon.WaitSSOLoginRequest
|
3, // 18: daemon.DaemonService.WaitSSOLogin:input_type -> daemon.WaitSSOLoginRequest
|
||||||
5, // 19: daemon.DaemonService.Up:input_type -> daemon.UpRequest
|
5, // 19: daemon.DaemonService.Up:input_type -> daemon.UpRequest
|
||||||
7, // 20: daemon.DaemonService.Status:input_type -> daemon.StatusRequest
|
7, // 20: daemon.DaemonService.Status:input_type -> daemon.StatusRequest
|
||||||
9, // 21: daemon.DaemonService.Down:input_type -> daemon.DownRequest
|
9, // 21: daemon.DaemonService.Down:input_type -> daemon.DownRequest
|
||||||
11, // 22: daemon.DaemonService.GetConfig:input_type -> daemon.GetConfigRequest
|
11, // 22: daemon.DaemonService.GetConfig:input_type -> daemon.GetConfigRequest
|
||||||
20, // 23: daemon.DaemonService.ListRoutes:input_type -> daemon.ListRoutesRequest
|
20, // 23: daemon.DaemonService.ListNetworks:input_type -> daemon.ListNetworksRequest
|
||||||
22, // 24: daemon.DaemonService.SelectRoutes:input_type -> daemon.SelectRoutesRequest
|
22, // 24: daemon.DaemonService.SelectNetworks:input_type -> daemon.SelectNetworksRequest
|
||||||
22, // 25: daemon.DaemonService.DeselectRoutes:input_type -> daemon.SelectRoutesRequest
|
22, // 25: daemon.DaemonService.DeselectNetworks:input_type -> daemon.SelectNetworksRequest
|
||||||
26, // 26: daemon.DaemonService.DebugBundle:input_type -> daemon.DebugBundleRequest
|
26, // 26: daemon.DaemonService.DebugBundle:input_type -> daemon.DebugBundleRequest
|
||||||
28, // 27: daemon.DaemonService.GetLogLevel:input_type -> daemon.GetLogLevelRequest
|
28, // 27: daemon.DaemonService.GetLogLevel:input_type -> daemon.GetLogLevelRequest
|
||||||
30, // 28: daemon.DaemonService.SetLogLevel:input_type -> daemon.SetLogLevelRequest
|
30, // 28: daemon.DaemonService.SetLogLevel:input_type -> daemon.SetLogLevelRequest
|
||||||
@ -3039,9 +3041,9 @@ var file_daemon_proto_depIdxs = []int32{
|
|||||||
8, // 36: daemon.DaemonService.Status:output_type -> daemon.StatusResponse
|
8, // 36: daemon.DaemonService.Status:output_type -> daemon.StatusResponse
|
||||||
10, // 37: daemon.DaemonService.Down:output_type -> daemon.DownResponse
|
10, // 37: daemon.DaemonService.Down:output_type -> daemon.DownResponse
|
||||||
12, // 38: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse
|
12, // 38: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse
|
||||||
21, // 39: daemon.DaemonService.ListRoutes:output_type -> daemon.ListRoutesResponse
|
21, // 39: daemon.DaemonService.ListNetworks:output_type -> daemon.ListNetworksResponse
|
||||||
23, // 40: daemon.DaemonService.SelectRoutes:output_type -> daemon.SelectRoutesResponse
|
23, // 40: daemon.DaemonService.SelectNetworks:output_type -> daemon.SelectNetworksResponse
|
||||||
23, // 41: daemon.DaemonService.DeselectRoutes:output_type -> daemon.SelectRoutesResponse
|
23, // 41: daemon.DaemonService.DeselectNetworks:output_type -> daemon.SelectNetworksResponse
|
||||||
27, // 42: daemon.DaemonService.DebugBundle:output_type -> daemon.DebugBundleResponse
|
27, // 42: daemon.DaemonService.DebugBundle:output_type -> daemon.DebugBundleResponse
|
||||||
29, // 43: daemon.DaemonService.GetLogLevel:output_type -> daemon.GetLogLevelResponse
|
29, // 43: daemon.DaemonService.GetLogLevel:output_type -> daemon.GetLogLevelResponse
|
||||||
31, // 44: daemon.DaemonService.SetLogLevel:output_type -> daemon.SetLogLevelResponse
|
31, // 44: daemon.DaemonService.SetLogLevel:output_type -> daemon.SetLogLevelResponse
|
||||||
@ -3291,7 +3293,7 @@ func file_daemon_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_daemon_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
|
file_daemon_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*ListRoutesRequest); i {
|
switch v := v.(*ListNetworksRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@ -3303,7 +3305,7 @@ func file_daemon_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_daemon_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
|
file_daemon_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*ListRoutesResponse); i {
|
switch v := v.(*ListNetworksResponse); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@ -3315,7 +3317,7 @@ func file_daemon_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_daemon_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
|
file_daemon_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*SelectRoutesRequest); i {
|
switch v := v.(*SelectNetworksRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@ -3327,7 +3329,7 @@ func file_daemon_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_daemon_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
|
file_daemon_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*SelectRoutesResponse); i {
|
switch v := v.(*SelectNetworksResponse); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@ -3351,7 +3353,7 @@ func file_daemon_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_daemon_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
|
file_daemon_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*Route); i {
|
switch v := v.(*Network); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -28,14 +28,14 @@ service DaemonService {
|
|||||||
// GetConfig of the daemon.
|
// GetConfig of the daemon.
|
||||||
rpc GetConfig(GetConfigRequest) returns (GetConfigResponse) {}
|
rpc GetConfig(GetConfigRequest) returns (GetConfigResponse) {}
|
||||||
|
|
||||||
// List available network routes
|
// List available networks
|
||||||
rpc ListRoutes(ListRoutesRequest) returns (ListRoutesResponse) {}
|
rpc ListNetworks(ListNetworksRequest) returns (ListNetworksResponse) {}
|
||||||
|
|
||||||
// Select specific routes
|
// Select specific routes
|
||||||
rpc SelectRoutes(SelectRoutesRequest) returns (SelectRoutesResponse) {}
|
rpc SelectNetworks(SelectNetworksRequest) returns (SelectNetworksResponse) {}
|
||||||
|
|
||||||
// Deselect specific routes
|
// Deselect specific routes
|
||||||
rpc DeselectRoutes(SelectRoutesRequest) returns (SelectRoutesResponse) {}
|
rpc DeselectNetworks(SelectNetworksRequest) returns (SelectNetworksResponse) {}
|
||||||
|
|
||||||
// DebugBundle creates a debug bundle
|
// DebugBundle creates a debug bundle
|
||||||
rpc DebugBundle(DebugBundleRequest) returns (DebugBundleResponse) {}
|
rpc DebugBundle(DebugBundleRequest) returns (DebugBundleResponse) {}
|
||||||
@ -190,7 +190,7 @@ message PeerState {
|
|||||||
int64 bytesRx = 13;
|
int64 bytesRx = 13;
|
||||||
int64 bytesTx = 14;
|
int64 bytesTx = 14;
|
||||||
bool rosenpassEnabled = 15;
|
bool rosenpassEnabled = 15;
|
||||||
repeated string routes = 16;
|
repeated string networks = 16;
|
||||||
google.protobuf.Duration latency = 17;
|
google.protobuf.Duration latency = 17;
|
||||||
string relayAddress = 18;
|
string relayAddress = 18;
|
||||||
}
|
}
|
||||||
@ -203,7 +203,7 @@ message LocalPeerState {
|
|||||||
string fqdn = 4;
|
string fqdn = 4;
|
||||||
bool rosenpassEnabled = 5;
|
bool rosenpassEnabled = 5;
|
||||||
bool rosenpassPermissive = 6;
|
bool rosenpassPermissive = 6;
|
||||||
repeated string routes = 7;
|
repeated string networks = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignalState contains the latest state of a signal connection
|
// SignalState contains the latest state of a signal connection
|
||||||
@ -244,20 +244,20 @@ message FullStatus {
|
|||||||
repeated NSGroupState dns_servers = 6;
|
repeated NSGroupState dns_servers = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListRoutesRequest {
|
message ListNetworksRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListRoutesResponse {
|
message ListNetworksResponse {
|
||||||
repeated Route routes = 1;
|
repeated Network routes = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SelectRoutesRequest {
|
message SelectNetworksRequest {
|
||||||
repeated string routeIDs = 1;
|
repeated string networkIDs = 1;
|
||||||
bool append = 2;
|
bool append = 2;
|
||||||
bool all = 3;
|
bool all = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SelectRoutesResponse {
|
message SelectNetworksResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
message IPList {
|
message IPList {
|
||||||
@ -265,9 +265,9 @@ message IPList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
message Route {
|
message Network {
|
||||||
string ID = 1;
|
string ID = 1;
|
||||||
string network = 2;
|
string range = 2;
|
||||||
bool selected = 3;
|
bool selected = 3;
|
||||||
repeated string domains = 4;
|
repeated string domains = 4;
|
||||||
map<string, IPList> resolvedIPs = 5;
|
map<string, IPList> resolvedIPs = 5;
|
||||||
|
@ -31,12 +31,12 @@ type DaemonServiceClient interface {
|
|||||||
Down(ctx context.Context, in *DownRequest, opts ...grpc.CallOption) (*DownResponse, error)
|
Down(ctx context.Context, in *DownRequest, opts ...grpc.CallOption) (*DownResponse, error)
|
||||||
// GetConfig of the daemon.
|
// GetConfig of the daemon.
|
||||||
GetConfig(ctx context.Context, in *GetConfigRequest, opts ...grpc.CallOption) (*GetConfigResponse, error)
|
GetConfig(ctx context.Context, in *GetConfigRequest, opts ...grpc.CallOption) (*GetConfigResponse, error)
|
||||||
// List available network routes
|
// List available networks
|
||||||
ListRoutes(ctx context.Context, in *ListRoutesRequest, opts ...grpc.CallOption) (*ListRoutesResponse, error)
|
ListNetworks(ctx context.Context, in *ListNetworksRequest, opts ...grpc.CallOption) (*ListNetworksResponse, error)
|
||||||
// Select specific routes
|
// Select specific routes
|
||||||
SelectRoutes(ctx context.Context, in *SelectRoutesRequest, opts ...grpc.CallOption) (*SelectRoutesResponse, error)
|
SelectNetworks(ctx context.Context, in *SelectNetworksRequest, opts ...grpc.CallOption) (*SelectNetworksResponse, error)
|
||||||
// Deselect specific routes
|
// Deselect specific routes
|
||||||
DeselectRoutes(ctx context.Context, in *SelectRoutesRequest, opts ...grpc.CallOption) (*SelectRoutesResponse, error)
|
DeselectNetworks(ctx context.Context, in *SelectNetworksRequest, opts ...grpc.CallOption) (*SelectNetworksResponse, error)
|
||||||
// DebugBundle creates a debug bundle
|
// DebugBundle creates a debug bundle
|
||||||
DebugBundle(ctx context.Context, in *DebugBundleRequest, opts ...grpc.CallOption) (*DebugBundleResponse, error)
|
DebugBundle(ctx context.Context, in *DebugBundleRequest, opts ...grpc.CallOption) (*DebugBundleResponse, error)
|
||||||
// GetLogLevel gets the log level of the daemon
|
// GetLogLevel gets the log level of the daemon
|
||||||
@ -115,27 +115,27 @@ func (c *daemonServiceClient) GetConfig(ctx context.Context, in *GetConfigReques
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *daemonServiceClient) ListRoutes(ctx context.Context, in *ListRoutesRequest, opts ...grpc.CallOption) (*ListRoutesResponse, error) {
|
func (c *daemonServiceClient) ListNetworks(ctx context.Context, in *ListNetworksRequest, opts ...grpc.CallOption) (*ListNetworksResponse, error) {
|
||||||
out := new(ListRoutesResponse)
|
out := new(ListNetworksResponse)
|
||||||
err := c.cc.Invoke(ctx, "/daemon.DaemonService/ListRoutes", in, out, opts...)
|
err := c.cc.Invoke(ctx, "/daemon.DaemonService/ListNetworks", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *daemonServiceClient) SelectRoutes(ctx context.Context, in *SelectRoutesRequest, opts ...grpc.CallOption) (*SelectRoutesResponse, error) {
|
func (c *daemonServiceClient) SelectNetworks(ctx context.Context, in *SelectNetworksRequest, opts ...grpc.CallOption) (*SelectNetworksResponse, error) {
|
||||||
out := new(SelectRoutesResponse)
|
out := new(SelectNetworksResponse)
|
||||||
err := c.cc.Invoke(ctx, "/daemon.DaemonService/SelectRoutes", in, out, opts...)
|
err := c.cc.Invoke(ctx, "/daemon.DaemonService/SelectNetworks", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *daemonServiceClient) DeselectRoutes(ctx context.Context, in *SelectRoutesRequest, opts ...grpc.CallOption) (*SelectRoutesResponse, error) {
|
func (c *daemonServiceClient) DeselectNetworks(ctx context.Context, in *SelectNetworksRequest, opts ...grpc.CallOption) (*SelectNetworksResponse, error) {
|
||||||
out := new(SelectRoutesResponse)
|
out := new(SelectNetworksResponse)
|
||||||
err := c.cc.Invoke(ctx, "/daemon.DaemonService/DeselectRoutes", in, out, opts...)
|
err := c.cc.Invoke(ctx, "/daemon.DaemonService/DeselectNetworks", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -222,12 +222,12 @@ type DaemonServiceServer interface {
|
|||||||
Down(context.Context, *DownRequest) (*DownResponse, error)
|
Down(context.Context, *DownRequest) (*DownResponse, error)
|
||||||
// GetConfig of the daemon.
|
// GetConfig of the daemon.
|
||||||
GetConfig(context.Context, *GetConfigRequest) (*GetConfigResponse, error)
|
GetConfig(context.Context, *GetConfigRequest) (*GetConfigResponse, error)
|
||||||
// List available network routes
|
// List available networks
|
||||||
ListRoutes(context.Context, *ListRoutesRequest) (*ListRoutesResponse, error)
|
ListNetworks(context.Context, *ListNetworksRequest) (*ListNetworksResponse, error)
|
||||||
// Select specific routes
|
// Select specific routes
|
||||||
SelectRoutes(context.Context, *SelectRoutesRequest) (*SelectRoutesResponse, error)
|
SelectNetworks(context.Context, *SelectNetworksRequest) (*SelectNetworksResponse, error)
|
||||||
// Deselect specific routes
|
// Deselect specific routes
|
||||||
DeselectRoutes(context.Context, *SelectRoutesRequest) (*SelectRoutesResponse, error)
|
DeselectNetworks(context.Context, *SelectNetworksRequest) (*SelectNetworksResponse, error)
|
||||||
// DebugBundle creates a debug bundle
|
// DebugBundle creates a debug bundle
|
||||||
DebugBundle(context.Context, *DebugBundleRequest) (*DebugBundleResponse, error)
|
DebugBundle(context.Context, *DebugBundleRequest) (*DebugBundleResponse, error)
|
||||||
// GetLogLevel gets the log level of the daemon
|
// GetLogLevel gets the log level of the daemon
|
||||||
@ -267,14 +267,14 @@ func (UnimplementedDaemonServiceServer) Down(context.Context, *DownRequest) (*Do
|
|||||||
func (UnimplementedDaemonServiceServer) GetConfig(context.Context, *GetConfigRequest) (*GetConfigResponse, error) {
|
func (UnimplementedDaemonServiceServer) GetConfig(context.Context, *GetConfigRequest) (*GetConfigResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetConfig not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method GetConfig not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedDaemonServiceServer) ListRoutes(context.Context, *ListRoutesRequest) (*ListRoutesResponse, error) {
|
func (UnimplementedDaemonServiceServer) ListNetworks(context.Context, *ListNetworksRequest) (*ListNetworksResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method ListRoutes not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method ListNetworks not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedDaemonServiceServer) SelectRoutes(context.Context, *SelectRoutesRequest) (*SelectRoutesResponse, error) {
|
func (UnimplementedDaemonServiceServer) SelectNetworks(context.Context, *SelectNetworksRequest) (*SelectNetworksResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method SelectRoutes not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method SelectNetworks not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedDaemonServiceServer) DeselectRoutes(context.Context, *SelectRoutesRequest) (*SelectRoutesResponse, error) {
|
func (UnimplementedDaemonServiceServer) DeselectNetworks(context.Context, *SelectNetworksRequest) (*SelectNetworksResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method DeselectRoutes not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method DeselectNetworks not implemented")
|
||||||
}
|
}
|
||||||
func (UnimplementedDaemonServiceServer) DebugBundle(context.Context, *DebugBundleRequest) (*DebugBundleResponse, error) {
|
func (UnimplementedDaemonServiceServer) DebugBundle(context.Context, *DebugBundleRequest) (*DebugBundleResponse, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method DebugBundle not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method DebugBundle not implemented")
|
||||||
@ -418,56 +418,56 @@ func _DaemonService_GetConfig_Handler(srv interface{}, ctx context.Context, dec
|
|||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _DaemonService_ListRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _DaemonService_ListNetworks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(ListRoutesRequest)
|
in := new(ListNetworksRequest)
|
||||||
if err := dec(in); err != nil {
|
if err := dec(in); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if interceptor == nil {
|
if interceptor == nil {
|
||||||
return srv.(DaemonServiceServer).ListRoutes(ctx, in)
|
return srv.(DaemonServiceServer).ListNetworks(ctx, in)
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/daemon.DaemonService/ListRoutes",
|
FullMethod: "/daemon.DaemonService/ListNetworks",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(DaemonServiceServer).ListRoutes(ctx, req.(*ListRoutesRequest))
|
return srv.(DaemonServiceServer).ListNetworks(ctx, req.(*ListNetworksRequest))
|
||||||
}
|
}
|
||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _DaemonService_SelectRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _DaemonService_SelectNetworks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(SelectRoutesRequest)
|
in := new(SelectNetworksRequest)
|
||||||
if err := dec(in); err != nil {
|
if err := dec(in); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if interceptor == nil {
|
if interceptor == nil {
|
||||||
return srv.(DaemonServiceServer).SelectRoutes(ctx, in)
|
return srv.(DaemonServiceServer).SelectNetworks(ctx, in)
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/daemon.DaemonService/SelectRoutes",
|
FullMethod: "/daemon.DaemonService/SelectNetworks",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(DaemonServiceServer).SelectRoutes(ctx, req.(*SelectRoutesRequest))
|
return srv.(DaemonServiceServer).SelectNetworks(ctx, req.(*SelectNetworksRequest))
|
||||||
}
|
}
|
||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _DaemonService_DeselectRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _DaemonService_DeselectNetworks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(SelectRoutesRequest)
|
in := new(SelectNetworksRequest)
|
||||||
if err := dec(in); err != nil {
|
if err := dec(in); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if interceptor == nil {
|
if interceptor == nil {
|
||||||
return srv.(DaemonServiceServer).DeselectRoutes(ctx, in)
|
return srv.(DaemonServiceServer).DeselectNetworks(ctx, in)
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/daemon.DaemonService/DeselectRoutes",
|
FullMethod: "/daemon.DaemonService/DeselectNetworks",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(DaemonServiceServer).DeselectRoutes(ctx, req.(*SelectRoutesRequest))
|
return srv.(DaemonServiceServer).DeselectNetworks(ctx, req.(*SelectNetworksRequest))
|
||||||
}
|
}
|
||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
@ -630,16 +630,16 @@ var DaemonService_ServiceDesc = grpc.ServiceDesc{
|
|||||||
Handler: _DaemonService_GetConfig_Handler,
|
Handler: _DaemonService_GetConfig_Handler,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MethodName: "ListRoutes",
|
MethodName: "ListNetworks",
|
||||||
Handler: _DaemonService_ListRoutes_Handler,
|
Handler: _DaemonService_ListNetworks_Handler,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MethodName: "SelectRoutes",
|
MethodName: "SelectNetworks",
|
||||||
Handler: _DaemonService_SelectRoutes_Handler,
|
Handler: _DaemonService_SelectNetworks_Handler,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MethodName: "DeselectRoutes",
|
MethodName: "DeselectNetworks",
|
||||||
Handler: _DaemonService_DeselectRoutes_Handler,
|
Handler: _DaemonService_DeselectNetworks_Handler,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
MethodName: "DebugBundle",
|
MethodName: "DebugBundle",
|
||||||
|
@ -40,6 +40,8 @@ netbird.err: Most recent, anonymized stderr log file of the NetBird client.
|
|||||||
netbird.out: Most recent, anonymized stdout log file of the NetBird client.
|
netbird.out: Most recent, anonymized stdout log file of the NetBird client.
|
||||||
routes.txt: Anonymized system routes, if --system-info flag was provided.
|
routes.txt: Anonymized system routes, if --system-info flag was provided.
|
||||||
interfaces.txt: Anonymized network interface information, if --system-info flag was provided.
|
interfaces.txt: Anonymized network interface information, if --system-info flag was provided.
|
||||||
|
iptables.txt: Anonymized iptables rules with packet counters, if --system-info flag was provided.
|
||||||
|
nftables.txt: Anonymized nftables rules with packet counters, if --system-info flag was provided.
|
||||||
config.txt: Anonymized configuration information of the NetBird client.
|
config.txt: Anonymized configuration information of the NetBird client.
|
||||||
network_map.json: Anonymized network map containing peer configurations, routes, DNS settings, and firewall rules.
|
network_map.json: Anonymized network map containing peer configurations, routes, DNS settings, and firewall rules.
|
||||||
state.json: Anonymized client state dump containing netbird states.
|
state.json: Anonymized client state dump containing netbird states.
|
||||||
@ -106,6 +108,24 @@ The config.txt file contains anonymized configuration information of the NetBird
|
|||||||
- CustomDNSAddress
|
- CustomDNSAddress
|
||||||
|
|
||||||
Other non-sensitive configuration options are included without anonymization.
|
Other non-sensitive configuration options are included without anonymization.
|
||||||
|
|
||||||
|
Firewall Rules (Linux only)
|
||||||
|
The bundle includes two separate firewall rule files:
|
||||||
|
|
||||||
|
iptables.txt:
|
||||||
|
- Complete iptables ruleset with packet counters using 'iptables -v -n -L'
|
||||||
|
- Includes all tables (filter, nat, mangle, raw, security)
|
||||||
|
- Shows packet and byte counters for each rule
|
||||||
|
- All IP addresses are anonymized
|
||||||
|
- Chain names, table names, and other non-sensitive information remain unchanged
|
||||||
|
|
||||||
|
nftables.txt:
|
||||||
|
- Complete nftables ruleset obtained via 'nft -a list ruleset'
|
||||||
|
- Includes rule handle numbers and packet counters
|
||||||
|
- All tables, chains, and rules are included
|
||||||
|
- Shows packet and byte counters for each rule
|
||||||
|
- All IP addresses are anonymized
|
||||||
|
- Chain names, table names, and other non-sensitive information remain unchanged
|
||||||
`
|
`
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -172,6 +192,10 @@ func (s *Server) createArchive(bundlePath *os.File, req *proto.DebugBundleReques
|
|||||||
if err := s.addInterfaces(req, anonymizer, archive); err != nil {
|
if err := s.addInterfaces(req, anonymizer, archive); err != nil {
|
||||||
log.Errorf("Failed to add interfaces to debug bundle: %v", err)
|
log.Errorf("Failed to add interfaces to debug bundle: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := s.addFirewallRules(req, anonymizer, archive); err != nil {
|
||||||
|
log.Errorf("Failed to add firewall rules to debug bundle: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.addNetworkMap(req, anonymizer, archive); err != nil {
|
if err := s.addNetworkMap(req, anonymizer, archive); err != nil {
|
||||||
|
693
client/server/debug_linux.go
Normal file
693
client/server/debug_linux.go
Normal file
@ -0,0 +1,693 @@
|
|||||||
|
//go:build linux && !android
|
||||||
|
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/nftables"
|
||||||
|
"github.com/google/nftables/expr"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/anonymize"
|
||||||
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// addFirewallRules collects and adds firewall rules to the archive
|
||||||
|
func (s *Server) addFirewallRules(req *proto.DebugBundleRequest, anonymizer *anonymize.Anonymizer, archive *zip.Writer) error {
|
||||||
|
log.Info("Collecting firewall rules")
|
||||||
|
// Collect and add iptables rules
|
||||||
|
iptablesRules, err := collectIPTablesRules()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to collect iptables rules: %v", err)
|
||||||
|
} else {
|
||||||
|
if req.GetAnonymize() {
|
||||||
|
iptablesRules = anonymizer.AnonymizeString(iptablesRules)
|
||||||
|
}
|
||||||
|
if err := addFileToZip(archive, strings.NewReader(iptablesRules), "iptables.txt"); err != nil {
|
||||||
|
log.Warnf("Failed to add iptables rules to bundle: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect and add nftables rules
|
||||||
|
nftablesRules, err := collectNFTablesRules()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to collect nftables rules: %v", err)
|
||||||
|
} else {
|
||||||
|
if req.GetAnonymize() {
|
||||||
|
nftablesRules = anonymizer.AnonymizeString(nftablesRules)
|
||||||
|
}
|
||||||
|
if err := addFileToZip(archive, strings.NewReader(nftablesRules), "nftables.txt"); err != nil {
|
||||||
|
log.Warnf("Failed to add nftables rules to bundle: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// collectIPTablesRules collects rules using both iptables-save and verbose listing
|
||||||
|
func collectIPTablesRules() (string, error) {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
// First try using iptables-save
|
||||||
|
saveOutput, err := collectIPTablesSave()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to collect iptables rules using iptables-save: %v", err)
|
||||||
|
} else {
|
||||||
|
builder.WriteString("=== iptables-save output ===\n")
|
||||||
|
builder.WriteString(saveOutput)
|
||||||
|
builder.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then get verbose statistics for each table
|
||||||
|
builder.WriteString("=== iptables -v -n -L output ===\n")
|
||||||
|
|
||||||
|
// Get list of tables
|
||||||
|
tables := []string{"filter", "nat", "mangle", "raw", "security"}
|
||||||
|
|
||||||
|
for _, table := range tables {
|
||||||
|
builder.WriteString(fmt.Sprintf("*%s\n", table))
|
||||||
|
|
||||||
|
// Get verbose statistics for the entire table
|
||||||
|
stats, err := getTableStatistics(table)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to get statistics for table %s: %v", table, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
builder.WriteString(stats)
|
||||||
|
builder.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// collectIPTablesSave uses iptables-save to get rule definitions
|
||||||
|
func collectIPTablesSave() (string, error) {
|
||||||
|
cmd := exec.Command("iptables-save")
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return "", fmt.Errorf("execute iptables-save: %w (stderr: %s)", err, stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
rules := stdout.String()
|
||||||
|
if strings.TrimSpace(rules) == "" {
|
||||||
|
return "", fmt.Errorf("no iptables rules found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTableStatistics gets verbose statistics for an entire table using iptables command
|
||||||
|
func getTableStatistics(table string) (string, error) {
|
||||||
|
cmd := exec.Command("iptables", "-v", "-n", "-L", "-t", table)
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return "", fmt.Errorf("execute iptables -v -n -L: %w (stderr: %s)", err, stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return stdout.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// collectNFTablesRules attempts to collect nftables rules using either nft command or netlink
|
||||||
|
func collectNFTablesRules() (string, error) {
|
||||||
|
// First try using nft command
|
||||||
|
rules, err := collectNFTablesFromCommand()
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("Failed to collect nftables rules using nft command: %v, falling back to netlink", err)
|
||||||
|
// Fall back to netlink
|
||||||
|
rules, err = collectNFTablesFromNetlink()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("collect nftables rules using both nft and netlink failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rules, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// collectNFTablesFromCommand attempts to collect rules using nft command
|
||||||
|
func collectNFTablesFromCommand() (string, error) {
|
||||||
|
cmd := exec.Command("nft", "-a", "list", "ruleset")
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return "", fmt.Errorf("execute nft list ruleset: %w (stderr: %s)", err, stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
rules := stdout.String()
|
||||||
|
if strings.TrimSpace(rules) == "" {
|
||||||
|
return "", fmt.Errorf("no nftables rules found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return rules, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// collectNFTablesFromNetlink collects rules using netlink library
|
||||||
|
func collectNFTablesFromNetlink() (string, error) {
|
||||||
|
conn, err := nftables.New()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("create nftables connection: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tables, err := conn.ListTables()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("list tables: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sortTables(tables)
|
||||||
|
return formatTables(conn, tables), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatTables(conn *nftables.Conn, tables []*nftables.Table) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
for _, table := range tables {
|
||||||
|
builder.WriteString(fmt.Sprintf("table %s %s {\n", formatFamily(table.Family), table.Name))
|
||||||
|
|
||||||
|
chains, err := getAndSortTableChains(conn, table)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to list chains for table %s: %v", table.Name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format chains
|
||||||
|
for _, chain := range chains {
|
||||||
|
formatChain(conn, table, chain, &builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format sets
|
||||||
|
if sets, err := conn.GetSets(table); err != nil {
|
||||||
|
log.Warnf("Failed to get sets for table %s: %v", table.Name, err)
|
||||||
|
} else if len(sets) > 0 {
|
||||||
|
builder.WriteString("\n")
|
||||||
|
for _, set := range sets {
|
||||||
|
builder.WriteString(formatSet(conn, set))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString("}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAndSortTableChains(conn *nftables.Conn, table *nftables.Table) ([]*nftables.Chain, error) {
|
||||||
|
chains, err := conn.ListChains()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var tableChains []*nftables.Chain
|
||||||
|
for _, chain := range chains {
|
||||||
|
if chain.Table.Name == table.Name && chain.Table.Family == table.Family {
|
||||||
|
tableChains = append(tableChains, chain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(tableChains, func(i, j int) bool {
|
||||||
|
return tableChains[i].Name < tableChains[j].Name
|
||||||
|
})
|
||||||
|
|
||||||
|
return tableChains, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatChain(conn *nftables.Conn, table *nftables.Table, chain *nftables.Chain, builder *strings.Builder) {
|
||||||
|
builder.WriteString(fmt.Sprintf("\tchain %s {\n", chain.Name))
|
||||||
|
|
||||||
|
if chain.Type != "" {
|
||||||
|
var policy string
|
||||||
|
if chain.Policy != nil {
|
||||||
|
policy = fmt.Sprintf("; policy %s", formatPolicy(*chain.Policy))
|
||||||
|
}
|
||||||
|
builder.WriteString(fmt.Sprintf("\t\ttype %s hook %s priority %d%s\n",
|
||||||
|
formatChainType(chain.Type),
|
||||||
|
formatChainHook(chain.Hooknum),
|
||||||
|
chain.Priority,
|
||||||
|
policy))
|
||||||
|
}
|
||||||
|
|
||||||
|
rules, err := conn.GetRules(table, chain)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to get rules for chain %s: %v", chain.Name, err)
|
||||||
|
} else {
|
||||||
|
sort.Slice(rules, func(i, j int) bool {
|
||||||
|
return rules[i].Position < rules[j].Position
|
||||||
|
})
|
||||||
|
for _, rule := range rules {
|
||||||
|
builder.WriteString(formatRule(rule))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString("\t}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortTables(tables []*nftables.Table) {
|
||||||
|
sort.Slice(tables, func(i, j int) bool {
|
||||||
|
if tables[i].Family != tables[j].Family {
|
||||||
|
return tables[i].Family < tables[j].Family
|
||||||
|
}
|
||||||
|
return tables[i].Name < tables[j].Name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatFamily(family nftables.TableFamily) string {
|
||||||
|
switch family {
|
||||||
|
case nftables.TableFamilyIPv4:
|
||||||
|
return "ip"
|
||||||
|
case nftables.TableFamilyIPv6:
|
||||||
|
return "ip6"
|
||||||
|
case nftables.TableFamilyINet:
|
||||||
|
return "inet"
|
||||||
|
case nftables.TableFamilyARP:
|
||||||
|
return "arp"
|
||||||
|
case nftables.TableFamilyBridge:
|
||||||
|
return "bridge"
|
||||||
|
case nftables.TableFamilyNetdev:
|
||||||
|
return "netdev"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("family-%d", family)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatChainType(typ nftables.ChainType) string {
|
||||||
|
switch typ {
|
||||||
|
case nftables.ChainTypeFilter:
|
||||||
|
return "filter"
|
||||||
|
case nftables.ChainTypeNAT:
|
||||||
|
return "nat"
|
||||||
|
case nftables.ChainTypeRoute:
|
||||||
|
return "route"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("type-%s", typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatChainHook(hook *nftables.ChainHook) string {
|
||||||
|
if hook == nil {
|
||||||
|
return "none"
|
||||||
|
}
|
||||||
|
switch *hook {
|
||||||
|
case *nftables.ChainHookPrerouting:
|
||||||
|
return "prerouting"
|
||||||
|
case *nftables.ChainHookInput:
|
||||||
|
return "input"
|
||||||
|
case *nftables.ChainHookForward:
|
||||||
|
return "forward"
|
||||||
|
case *nftables.ChainHookOutput:
|
||||||
|
return "output"
|
||||||
|
case *nftables.ChainHookPostrouting:
|
||||||
|
return "postrouting"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("hook-%d", *hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatPolicy(policy nftables.ChainPolicy) string {
|
||||||
|
switch policy {
|
||||||
|
case nftables.ChainPolicyDrop:
|
||||||
|
return "drop"
|
||||||
|
case nftables.ChainPolicyAccept:
|
||||||
|
return "accept"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("policy-%d", policy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatRule(rule *nftables.Rule) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
builder.WriteString("\t\t")
|
||||||
|
|
||||||
|
for i := 0; i < len(rule.Exprs); i++ {
|
||||||
|
if i > 0 {
|
||||||
|
builder.WriteString(" ")
|
||||||
|
}
|
||||||
|
i = formatExprSequence(&builder, rule.Exprs, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString("\n")
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatExprSequence(builder *strings.Builder, exprs []expr.Any, i int) int {
|
||||||
|
curr := exprs[i]
|
||||||
|
|
||||||
|
// Handle Meta + Cmp sequence
|
||||||
|
if meta, ok := curr.(*expr.Meta); ok && i+1 < len(exprs) {
|
||||||
|
if cmp, ok := exprs[i+1].(*expr.Cmp); ok {
|
||||||
|
if formatted := formatMetaWithCmp(meta, cmp); formatted != "" {
|
||||||
|
builder.WriteString(formatted)
|
||||||
|
return i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Payload + Cmp sequence
|
||||||
|
if payload, ok := curr.(*expr.Payload); ok && i+1 < len(exprs) {
|
||||||
|
if cmp, ok := exprs[i+1].(*expr.Cmp); ok {
|
||||||
|
builder.WriteString(formatPayloadWithCmp(payload, cmp))
|
||||||
|
return i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString(formatExpr(curr))
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatMetaWithCmp(meta *expr.Meta, cmp *expr.Cmp) string {
|
||||||
|
switch meta.Key {
|
||||||
|
case expr.MetaKeyIIFNAME:
|
||||||
|
name := strings.TrimRight(string(cmp.Data), "\x00")
|
||||||
|
return fmt.Sprintf("iifname %s %q", formatCmpOp(cmp.Op), name)
|
||||||
|
case expr.MetaKeyOIFNAME:
|
||||||
|
name := strings.TrimRight(string(cmp.Data), "\x00")
|
||||||
|
return fmt.Sprintf("oifname %s %q", formatCmpOp(cmp.Op), name)
|
||||||
|
case expr.MetaKeyMARK:
|
||||||
|
if len(cmp.Data) == 4 {
|
||||||
|
val := binary.BigEndian.Uint32(cmp.Data)
|
||||||
|
return fmt.Sprintf("meta mark %s 0x%x", formatCmpOp(cmp.Op), val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatPayloadWithCmp(p *expr.Payload, cmp *expr.Cmp) string {
|
||||||
|
if p.Base == expr.PayloadBaseNetworkHeader {
|
||||||
|
switch p.Offset {
|
||||||
|
case 12: // Source IP
|
||||||
|
if p.Len == 4 {
|
||||||
|
return fmt.Sprintf("ip saddr %s %s", formatCmpOp(cmp.Op), formatIPBytes(cmp.Data))
|
||||||
|
} else if p.Len == 2 {
|
||||||
|
return fmt.Sprintf("ip saddr %s %s", formatCmpOp(cmp.Op), formatIPBytes(cmp.Data))
|
||||||
|
}
|
||||||
|
case 16: // Destination IP
|
||||||
|
if p.Len == 4 {
|
||||||
|
return fmt.Sprintf("ip daddr %s %s", formatCmpOp(cmp.Op), formatIPBytes(cmp.Data))
|
||||||
|
} else if p.Len == 2 {
|
||||||
|
return fmt.Sprintf("ip daddr %s %s", formatCmpOp(cmp.Op), formatIPBytes(cmp.Data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d reg%d [%d:%d] %s %v",
|
||||||
|
p.Base, p.DestRegister, p.Offset, p.Len,
|
||||||
|
formatCmpOp(cmp.Op), cmp.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatIPBytes(data []byte) string {
|
||||||
|
if len(data) == 4 {
|
||||||
|
return fmt.Sprintf("%d.%d.%d.%d", data[0], data[1], data[2], data[3])
|
||||||
|
} else if len(data) == 2 {
|
||||||
|
return fmt.Sprintf("%d.%d.0.0/16", data[0], data[1])
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%v", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatCmpOp(op expr.CmpOp) string {
|
||||||
|
switch op {
|
||||||
|
case expr.CmpOpEq:
|
||||||
|
return "=="
|
||||||
|
case expr.CmpOpNeq:
|
||||||
|
return "!="
|
||||||
|
case expr.CmpOpLt:
|
||||||
|
return "<"
|
||||||
|
case expr.CmpOpLte:
|
||||||
|
return "<="
|
||||||
|
case expr.CmpOpGt:
|
||||||
|
return ">"
|
||||||
|
case expr.CmpOpGte:
|
||||||
|
return ">="
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("op-%d", op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatExpr formats an expression in nft-like syntax
|
||||||
|
func formatExpr(exp expr.Any) string {
|
||||||
|
switch e := exp.(type) {
|
||||||
|
case *expr.Meta:
|
||||||
|
return formatMeta(e)
|
||||||
|
case *expr.Cmp:
|
||||||
|
return formatCmp(e)
|
||||||
|
case *expr.Payload:
|
||||||
|
return formatPayload(e)
|
||||||
|
case *expr.Verdict:
|
||||||
|
return formatVerdict(e)
|
||||||
|
case *expr.Counter:
|
||||||
|
return fmt.Sprintf("counter packets %d bytes %d", e.Packets, e.Bytes)
|
||||||
|
case *expr.Masq:
|
||||||
|
return "masquerade"
|
||||||
|
case *expr.NAT:
|
||||||
|
return formatNat(e)
|
||||||
|
case *expr.Match:
|
||||||
|
return formatMatch(e)
|
||||||
|
case *expr.Queue:
|
||||||
|
return fmt.Sprintf("queue num %d", e.Num)
|
||||||
|
case *expr.Lookup:
|
||||||
|
return fmt.Sprintf("@%s", e.SetName)
|
||||||
|
case *expr.Bitwise:
|
||||||
|
return formatBitwise(e)
|
||||||
|
case *expr.Fib:
|
||||||
|
return formatFib(e)
|
||||||
|
case *expr.Target:
|
||||||
|
return fmt.Sprintf("jump %s", e.Name) // Properly format jump targets
|
||||||
|
case *expr.Immediate:
|
||||||
|
if e.Register == 1 {
|
||||||
|
return formatImmediateData(e.Data)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("immediate %v", e.Data)
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("<%T>", exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatImmediateData(data []byte) string {
|
||||||
|
// For IP addresses (4 bytes)
|
||||||
|
if len(data) == 4 {
|
||||||
|
return fmt.Sprintf("%d.%d.%d.%d", data[0], data[1], data[2], data[3])
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%v", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatMeta(e *expr.Meta) string {
|
||||||
|
// Handle source register case first (meta mark set)
|
||||||
|
if e.SourceRegister {
|
||||||
|
return fmt.Sprintf("meta %s set reg %d", formatMetaKey(e.Key), e.Register)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For interface names, handle register load operation
|
||||||
|
switch e.Key {
|
||||||
|
case expr.MetaKeyIIFNAME,
|
||||||
|
expr.MetaKeyOIFNAME,
|
||||||
|
expr.MetaKeyBRIIIFNAME,
|
||||||
|
expr.MetaKeyBRIOIFNAME:
|
||||||
|
// Simply the key name with no register reference
|
||||||
|
return formatMetaKey(e.Key)
|
||||||
|
|
||||||
|
case expr.MetaKeyMARK:
|
||||||
|
// For mark operations, we want just "mark"
|
||||||
|
return "mark"
|
||||||
|
}
|
||||||
|
|
||||||
|
// For other meta keys, show as loading into register
|
||||||
|
return fmt.Sprintf("meta %s => reg %d", formatMetaKey(e.Key), e.Register)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatMetaKey(key expr.MetaKey) string {
|
||||||
|
switch key {
|
||||||
|
case expr.MetaKeyLEN:
|
||||||
|
return "length"
|
||||||
|
case expr.MetaKeyPROTOCOL:
|
||||||
|
return "protocol"
|
||||||
|
case expr.MetaKeyPRIORITY:
|
||||||
|
return "priority"
|
||||||
|
case expr.MetaKeyMARK:
|
||||||
|
return "mark"
|
||||||
|
case expr.MetaKeyIIF:
|
||||||
|
return "iif"
|
||||||
|
case expr.MetaKeyOIF:
|
||||||
|
return "oif"
|
||||||
|
case expr.MetaKeyIIFNAME:
|
||||||
|
return "iifname"
|
||||||
|
case expr.MetaKeyOIFNAME:
|
||||||
|
return "oifname"
|
||||||
|
case expr.MetaKeyIIFTYPE:
|
||||||
|
return "iiftype"
|
||||||
|
case expr.MetaKeyOIFTYPE:
|
||||||
|
return "oiftype"
|
||||||
|
case expr.MetaKeySKUID:
|
||||||
|
return "skuid"
|
||||||
|
case expr.MetaKeySKGID:
|
||||||
|
return "skgid"
|
||||||
|
case expr.MetaKeyNFTRACE:
|
||||||
|
return "nftrace"
|
||||||
|
case expr.MetaKeyRTCLASSID:
|
||||||
|
return "rtclassid"
|
||||||
|
case expr.MetaKeySECMARK:
|
||||||
|
return "secmark"
|
||||||
|
case expr.MetaKeyNFPROTO:
|
||||||
|
return "nfproto"
|
||||||
|
case expr.MetaKeyL4PROTO:
|
||||||
|
return "l4proto"
|
||||||
|
case expr.MetaKeyBRIIIFNAME:
|
||||||
|
return "briifname"
|
||||||
|
case expr.MetaKeyBRIOIFNAME:
|
||||||
|
return "broifname"
|
||||||
|
case expr.MetaKeyPKTTYPE:
|
||||||
|
return "pkttype"
|
||||||
|
case expr.MetaKeyCPU:
|
||||||
|
return "cpu"
|
||||||
|
case expr.MetaKeyIIFGROUP:
|
||||||
|
return "iifgroup"
|
||||||
|
case expr.MetaKeyOIFGROUP:
|
||||||
|
return "oifgroup"
|
||||||
|
case expr.MetaKeyCGROUP:
|
||||||
|
return "cgroup"
|
||||||
|
case expr.MetaKeyPRANDOM:
|
||||||
|
return "prandom"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("meta-%d", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatCmp(e *expr.Cmp) string {
|
||||||
|
ops := map[expr.CmpOp]string{
|
||||||
|
expr.CmpOpEq: "==",
|
||||||
|
expr.CmpOpNeq: "!=",
|
||||||
|
expr.CmpOpLt: "<",
|
||||||
|
expr.CmpOpLte: "<=",
|
||||||
|
expr.CmpOpGt: ">",
|
||||||
|
expr.CmpOpGte: ">=",
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s %v", ops[e.Op], e.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatPayload(e *expr.Payload) string {
|
||||||
|
var proto string
|
||||||
|
switch e.Base {
|
||||||
|
case expr.PayloadBaseNetworkHeader:
|
||||||
|
proto = "ip"
|
||||||
|
case expr.PayloadBaseTransportHeader:
|
||||||
|
proto = "tcp"
|
||||||
|
default:
|
||||||
|
proto = fmt.Sprintf("payload-%d", e.Base)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s reg%d [%d:%d]", proto, e.DestRegister, e.Offset, e.Len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatVerdict(e *expr.Verdict) string {
|
||||||
|
switch e.Kind {
|
||||||
|
case expr.VerdictAccept:
|
||||||
|
return "accept"
|
||||||
|
case expr.VerdictDrop:
|
||||||
|
return "drop"
|
||||||
|
case expr.VerdictJump:
|
||||||
|
return fmt.Sprintf("jump %s", e.Chain)
|
||||||
|
case expr.VerdictGoto:
|
||||||
|
return fmt.Sprintf("goto %s", e.Chain)
|
||||||
|
case expr.VerdictReturn:
|
||||||
|
return "return"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("verdict-%d", e.Kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatNat(e *expr.NAT) string {
|
||||||
|
switch e.Type {
|
||||||
|
case expr.NATTypeSourceNAT:
|
||||||
|
return "snat"
|
||||||
|
case expr.NATTypeDestNAT:
|
||||||
|
return "dnat"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("nat-%d", e.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatMatch(e *expr.Match) string {
|
||||||
|
return fmt.Sprintf("match %s rev %d", e.Name, e.Rev)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatBitwise(e *expr.Bitwise) string {
|
||||||
|
return fmt.Sprintf("bitwise reg%d = reg%d & %v ^ %v",
|
||||||
|
e.DestRegister, e.SourceRegister, e.Mask, e.Xor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatFib(e *expr.Fib) string {
|
||||||
|
var flags []string
|
||||||
|
if e.FlagSADDR {
|
||||||
|
flags = append(flags, "saddr")
|
||||||
|
}
|
||||||
|
if e.FlagDADDR {
|
||||||
|
flags = append(flags, "daddr")
|
||||||
|
}
|
||||||
|
if e.FlagMARK {
|
||||||
|
flags = append(flags, "mark")
|
||||||
|
}
|
||||||
|
if e.FlagIIF {
|
||||||
|
flags = append(flags, "iif")
|
||||||
|
}
|
||||||
|
if e.FlagOIF {
|
||||||
|
flags = append(flags, "oif")
|
||||||
|
}
|
||||||
|
if e.ResultADDRTYPE {
|
||||||
|
flags = append(flags, "type")
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("fib reg%d %s", e.Register, strings.Join(flags, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatSet(conn *nftables.Conn, set *nftables.Set) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
builder.WriteString(fmt.Sprintf("\tset %s {\n", set.Name))
|
||||||
|
builder.WriteString(fmt.Sprintf("\t\ttype %s\n", formatSetKeyType(set.KeyType)))
|
||||||
|
if set.ID > 0 {
|
||||||
|
builder.WriteString(fmt.Sprintf("\t\t# handle %d\n", set.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
elements, err := conn.GetSetElements(set)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to get elements for set %s: %v", set.Name, err)
|
||||||
|
} else if len(elements) > 0 {
|
||||||
|
builder.WriteString("\t\telements = {")
|
||||||
|
for i, elem := range elements {
|
||||||
|
if i > 0 {
|
||||||
|
builder.WriteString(", ")
|
||||||
|
}
|
||||||
|
builder.WriteString(fmt.Sprintf("%v", elem.Key))
|
||||||
|
}
|
||||||
|
builder.WriteString("}\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString("\t}\n")
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatSetKeyType(keyType nftables.SetDatatype) string {
|
||||||
|
switch keyType {
|
||||||
|
case nftables.TypeInvalid:
|
||||||
|
return "invalid"
|
||||||
|
case nftables.TypeIPAddr:
|
||||||
|
return "ipv4_addr"
|
||||||
|
case nftables.TypeIP6Addr:
|
||||||
|
return "ipv6_addr"
|
||||||
|
case nftables.TypeEtherAddr:
|
||||||
|
return "ether_addr"
|
||||||
|
case nftables.TypeInetProto:
|
||||||
|
return "inet_proto"
|
||||||
|
case nftables.TypeInetService:
|
||||||
|
return "inet_service"
|
||||||
|
case nftables.TypeMark:
|
||||||
|
return "mark"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("type-%v", keyType)
|
||||||
|
}
|
||||||
|
}
|
15
client/server/debug_nonlinux.go
Normal file
15
client/server/debug_nonlinux.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//go:build !linux || android
|
||||||
|
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/anonymize"
|
||||||
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// collectFirewallRules returns nothing on non-linux systems
|
||||||
|
func (s *Server) addFirewallRules(req *proto.DebugBundleRequest, anonymizer *anonymize.Anonymizer, archive *zip.Writer) error {
|
||||||
|
return nil
|
||||||
|
}
|
@ -428,3 +428,116 @@ func isInCGNATRange(ip net.IP) bool {
|
|||||||
}
|
}
|
||||||
return cgnat.Contains(ip)
|
return cgnat.Contains(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAnonymizeFirewallRules(t *testing.T) {
|
||||||
|
// TODO: Add ipv6
|
||||||
|
|
||||||
|
// Example iptables-save output
|
||||||
|
iptablesSave := `# Generated by iptables-save v1.8.7 on Thu Dec 19 10:00:00 2024
|
||||||
|
*filter
|
||||||
|
:INPUT ACCEPT [0:0]
|
||||||
|
:FORWARD ACCEPT [0:0]
|
||||||
|
:OUTPUT ACCEPT [0:0]
|
||||||
|
-A INPUT -s 192.168.1.0/24 -j ACCEPT
|
||||||
|
-A INPUT -s 44.192.140.1/32 -j DROP
|
||||||
|
-A FORWARD -s 10.0.0.0/8 -j DROP
|
||||||
|
-A FORWARD -s 44.192.140.0/24 -d 52.84.12.34/24 -j ACCEPT
|
||||||
|
COMMIT
|
||||||
|
|
||||||
|
*nat
|
||||||
|
:PREROUTING ACCEPT [0:0]
|
||||||
|
:INPUT ACCEPT [0:0]
|
||||||
|
:OUTPUT ACCEPT [0:0]
|
||||||
|
:POSTROUTING ACCEPT [0:0]
|
||||||
|
-A POSTROUTING -s 192.168.100.0/24 -j MASQUERADE
|
||||||
|
-A PREROUTING -d 44.192.140.10/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.1.10:80
|
||||||
|
COMMIT`
|
||||||
|
|
||||||
|
// Example iptables -v -n -L output
|
||||||
|
iptablesVerbose := `Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
|
||||||
|
pkts bytes target prot opt in out source destination
|
||||||
|
0 0 ACCEPT all -- * * 192.168.1.0/24 0.0.0.0/0
|
||||||
|
100 1024 DROP all -- * * 44.192.140.1 0.0.0.0/0
|
||||||
|
|
||||||
|
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
|
||||||
|
pkts bytes target prot opt in out source destination
|
||||||
|
0 0 DROP all -- * * 10.0.0.0/8 0.0.0.0/0
|
||||||
|
25 256 ACCEPT all -- * * 44.192.140.0/24 52.84.12.34/24
|
||||||
|
|
||||||
|
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
|
||||||
|
pkts bytes target prot opt in out source destination`
|
||||||
|
|
||||||
|
// Example nftables output
|
||||||
|
nftablesRules := `table inet filter {
|
||||||
|
chain input {
|
||||||
|
type filter hook input priority filter; policy accept;
|
||||||
|
ip saddr 192.168.1.1 accept
|
||||||
|
ip saddr 44.192.140.1 drop
|
||||||
|
}
|
||||||
|
chain forward {
|
||||||
|
type filter hook forward priority filter; policy accept;
|
||||||
|
ip saddr 10.0.0.0/8 drop
|
||||||
|
ip saddr 44.192.140.0/24 ip daddr 52.84.12.34/24 accept
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
anonymizer := anonymize.NewAnonymizer(anonymize.DefaultAddresses())
|
||||||
|
|
||||||
|
// Test iptables-save anonymization
|
||||||
|
anonIptablesSave := anonymizer.AnonymizeString(iptablesSave)
|
||||||
|
|
||||||
|
// Private IP addresses should remain unchanged
|
||||||
|
assert.Contains(t, anonIptablesSave, "192.168.1.0/24")
|
||||||
|
assert.Contains(t, anonIptablesSave, "10.0.0.0/8")
|
||||||
|
assert.Contains(t, anonIptablesSave, "192.168.100.0/24")
|
||||||
|
assert.Contains(t, anonIptablesSave, "192.168.1.10")
|
||||||
|
|
||||||
|
// Public IP addresses should be anonymized to the default range
|
||||||
|
assert.NotContains(t, anonIptablesSave, "44.192.140.1")
|
||||||
|
assert.NotContains(t, anonIptablesSave, "44.192.140.0/24")
|
||||||
|
assert.NotContains(t, anonIptablesSave, "52.84.12.34")
|
||||||
|
assert.Contains(t, anonIptablesSave, "198.51.100.") // Default anonymous range
|
||||||
|
|
||||||
|
// Structure should be preserved
|
||||||
|
assert.Contains(t, anonIptablesSave, "*filter")
|
||||||
|
assert.Contains(t, anonIptablesSave, ":INPUT ACCEPT [0:0]")
|
||||||
|
assert.Contains(t, anonIptablesSave, "COMMIT")
|
||||||
|
assert.Contains(t, anonIptablesSave, "-j MASQUERADE")
|
||||||
|
assert.Contains(t, anonIptablesSave, "--dport 80")
|
||||||
|
|
||||||
|
// Test iptables verbose output anonymization
|
||||||
|
anonIptablesVerbose := anonymizer.AnonymizeString(iptablesVerbose)
|
||||||
|
|
||||||
|
// Private IP addresses should remain unchanged
|
||||||
|
assert.Contains(t, anonIptablesVerbose, "192.168.1.0/24")
|
||||||
|
assert.Contains(t, anonIptablesVerbose, "10.0.0.0/8")
|
||||||
|
|
||||||
|
// Public IP addresses should be anonymized to the default range
|
||||||
|
assert.NotContains(t, anonIptablesVerbose, "44.192.140.1")
|
||||||
|
assert.NotContains(t, anonIptablesVerbose, "44.192.140.0/24")
|
||||||
|
assert.NotContains(t, anonIptablesVerbose, "52.84.12.34")
|
||||||
|
assert.Contains(t, anonIptablesVerbose, "198.51.100.") // Default anonymous range
|
||||||
|
|
||||||
|
// Structure and counters should be preserved
|
||||||
|
assert.Contains(t, anonIptablesVerbose, "Chain INPUT (policy ACCEPT 0 packets, 0 bytes)")
|
||||||
|
assert.Contains(t, anonIptablesVerbose, "100 1024 DROP")
|
||||||
|
assert.Contains(t, anonIptablesVerbose, "pkts bytes target")
|
||||||
|
|
||||||
|
// Test nftables anonymization
|
||||||
|
anonNftables := anonymizer.AnonymizeString(nftablesRules)
|
||||||
|
|
||||||
|
// Private IP addresses should remain unchanged
|
||||||
|
assert.Contains(t, anonNftables, "192.168.1.1")
|
||||||
|
assert.Contains(t, anonNftables, "10.0.0.0/8")
|
||||||
|
|
||||||
|
// Public IP addresses should be anonymized to the default range
|
||||||
|
assert.NotContains(t, anonNftables, "44.192.140.1")
|
||||||
|
assert.NotContains(t, anonNftables, "44.192.140.0/24")
|
||||||
|
assert.NotContains(t, anonNftables, "52.84.12.34")
|
||||||
|
assert.Contains(t, anonNftables, "198.51.100.") // Default anonymous range
|
||||||
|
|
||||||
|
// Structure should be preserved
|
||||||
|
assert.Contains(t, anonNftables, "table inet filter {")
|
||||||
|
assert.Contains(t, anonNftables, "chain input {")
|
||||||
|
assert.Contains(t, anonNftables, "type filter hook input priority filter; policy accept;")
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
@ -20,8 +21,8 @@ type selectRoute struct {
|
|||||||
Selected bool
|
Selected bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListRoutes returns a list of all available routes.
|
// ListNetworks returns a list of all available networks.
|
||||||
func (s *Server) ListRoutes(context.Context, *proto.ListRoutesRequest) (*proto.ListRoutesResponse, error) {
|
func (s *Server) ListNetworks(context.Context, *proto.ListNetworksRequest) (*proto.ListNetworksResponse, error) {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
defer s.mutex.Unlock()
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ func (s *Server) ListRoutes(context.Context, *proto.ListRoutesRequest) (*proto.L
|
|||||||
return nil, fmt.Errorf("not connected")
|
return nil, fmt.Errorf("not connected")
|
||||||
}
|
}
|
||||||
|
|
||||||
routesMap := engine.GetClientRoutesWithNetID()
|
routesMap := engine.GetRouteManager().GetClientRoutesWithNetID()
|
||||||
routeSelector := engine.GetRouteManager().GetRouteSelector()
|
routeSelector := engine.GetRouteManager().GetRouteSelector()
|
||||||
|
|
||||||
var routes []*selectRoute
|
var routes []*selectRoute
|
||||||
@ -67,37 +68,47 @@ func (s *Server) ListRoutes(context.Context, *proto.ListRoutesRequest) (*proto.L
|
|||||||
})
|
})
|
||||||
|
|
||||||
resolvedDomains := s.statusRecorder.GetResolvedDomainsStates()
|
resolvedDomains := s.statusRecorder.GetResolvedDomainsStates()
|
||||||
var pbRoutes []*proto.Route
|
var pbRoutes []*proto.Network
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
pbRoute := &proto.Route{
|
pbRoute := &proto.Network{
|
||||||
ID: string(route.NetID),
|
ID: string(route.NetID),
|
||||||
Network: route.Network.String(),
|
Range: route.Network.String(),
|
||||||
Domains: route.Domains.ToSafeStringList(),
|
Domains: route.Domains.ToSafeStringList(),
|
||||||
ResolvedIPs: map[string]*proto.IPList{},
|
ResolvedIPs: map[string]*proto.IPList{},
|
||||||
Selected: route.Selected,
|
Selected: route.Selected,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, domain := range route.Domains {
|
// Group resolved IPs by their parent domain
|
||||||
if prefixes, exists := resolvedDomains[domain]; exists {
|
domainMap := map[domain.Domain][]string{}
|
||||||
var ipStrings []string
|
|
||||||
for _, prefix := range prefixes {
|
for resolvedDomain, info := range resolvedDomains {
|
||||||
ipStrings = append(ipStrings, prefix.Addr().String())
|
// Check if this resolved domain's parent is in our route's domains
|
||||||
}
|
if slices.Contains(route.Domains, info.ParentDomain) {
|
||||||
pbRoute.ResolvedIPs[string(domain)] = &proto.IPList{
|
ips := make([]string, 0, len(info.Prefixes))
|
||||||
Ips: ipStrings,
|
for _, prefix := range info.Prefixes {
|
||||||
|
ips = append(ips, prefix.Addr().String())
|
||||||
}
|
}
|
||||||
|
domainMap[resolvedDomain] = ips
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert to proto format
|
||||||
|
for domain, ips := range domainMap {
|
||||||
|
pbRoute.ResolvedIPs[string(domain)] = &proto.IPList{
|
||||||
|
Ips: ips,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pbRoutes = append(pbRoutes, pbRoute)
|
pbRoutes = append(pbRoutes, pbRoute)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &proto.ListRoutesResponse{
|
return &proto.ListNetworksResponse{
|
||||||
Routes: pbRoutes,
|
Routes: pbRoutes,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectRoutes selects specific routes based on the client request.
|
// SelectNetworks selects specific networks based on the client request.
|
||||||
func (s *Server) SelectRoutes(_ context.Context, req *proto.SelectRoutesRequest) (*proto.SelectRoutesResponse, error) {
|
func (s *Server) SelectNetworks(_ context.Context, req *proto.SelectNetworksRequest) (*proto.SelectNetworksResponse, error) {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
defer s.mutex.Unlock()
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
@ -115,18 +126,19 @@ func (s *Server) SelectRoutes(_ context.Context, req *proto.SelectRoutesRequest)
|
|||||||
if req.GetAll() {
|
if req.GetAll() {
|
||||||
routeSelector.SelectAllRoutes()
|
routeSelector.SelectAllRoutes()
|
||||||
} else {
|
} else {
|
||||||
routes := toNetIDs(req.GetRouteIDs())
|
routes := toNetIDs(req.GetNetworkIDs())
|
||||||
if err := routeSelector.SelectRoutes(routes, req.GetAppend(), maps.Keys(engine.GetClientRoutesWithNetID())); err != nil {
|
netIdRoutes := maps.Keys(routeManager.GetClientRoutesWithNetID())
|
||||||
|
if err := routeSelector.SelectRoutes(routes, req.GetAppend(), netIdRoutes); err != nil {
|
||||||
return nil, fmt.Errorf("select routes: %w", err)
|
return nil, fmt.Errorf("select routes: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
routeManager.TriggerSelection(engine.GetClientRoutes())
|
routeManager.TriggerSelection(routeManager.GetClientRoutes())
|
||||||
|
|
||||||
return &proto.SelectRoutesResponse{}, nil
|
return &proto.SelectNetworksResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeselectRoutes deselects specific routes based on the client request.
|
// DeselectNetworks deselects specific networks based on the client request.
|
||||||
func (s *Server) DeselectRoutes(_ context.Context, req *proto.SelectRoutesRequest) (*proto.SelectRoutesResponse, error) {
|
func (s *Server) DeselectNetworks(_ context.Context, req *proto.SelectNetworksRequest) (*proto.SelectNetworksResponse, error) {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
defer s.mutex.Unlock()
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
@ -144,14 +156,15 @@ func (s *Server) DeselectRoutes(_ context.Context, req *proto.SelectRoutesReques
|
|||||||
if req.GetAll() {
|
if req.GetAll() {
|
||||||
routeSelector.DeselectAllRoutes()
|
routeSelector.DeselectAllRoutes()
|
||||||
} else {
|
} else {
|
||||||
routes := toNetIDs(req.GetRouteIDs())
|
routes := toNetIDs(req.GetNetworkIDs())
|
||||||
if err := routeSelector.DeselectRoutes(routes, maps.Keys(engine.GetClientRoutesWithNetID())); err != nil {
|
netIdRoutes := maps.Keys(routeManager.GetClientRoutesWithNetID())
|
||||||
|
if err := routeSelector.DeselectRoutes(routes, netIdRoutes); err != nil {
|
||||||
return nil, fmt.Errorf("deselect routes: %w", err)
|
return nil, fmt.Errorf("deselect routes: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
routeManager.TriggerSelection(engine.GetClientRoutes())
|
routeManager.TriggerSelection(routeManager.GetClientRoutes())
|
||||||
|
|
||||||
return &proto.SelectRoutesResponse{}, nil
|
return &proto.SelectNetworksResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toNetIDs(routes []string) []route.NetID {
|
func toNetIDs(routes []string) []route.NetID {
|
@ -772,7 +772,7 @@ func toProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus {
|
|||||||
pbFullStatus.LocalPeerState.Fqdn = fullStatus.LocalPeerState.FQDN
|
pbFullStatus.LocalPeerState.Fqdn = fullStatus.LocalPeerState.FQDN
|
||||||
pbFullStatus.LocalPeerState.RosenpassPermissive = fullStatus.RosenpassState.Permissive
|
pbFullStatus.LocalPeerState.RosenpassPermissive = fullStatus.RosenpassState.Permissive
|
||||||
pbFullStatus.LocalPeerState.RosenpassEnabled = fullStatus.RosenpassState.Enabled
|
pbFullStatus.LocalPeerState.RosenpassEnabled = fullStatus.RosenpassState.Enabled
|
||||||
pbFullStatus.LocalPeerState.Routes = maps.Keys(fullStatus.LocalPeerState.Routes)
|
pbFullStatus.LocalPeerState.Networks = maps.Keys(fullStatus.LocalPeerState.Routes)
|
||||||
|
|
||||||
for _, peerState := range fullStatus.Peers {
|
for _, peerState := range fullStatus.Peers {
|
||||||
pbPeerState := &proto.PeerState{
|
pbPeerState := &proto.PeerState{
|
||||||
@ -791,7 +791,7 @@ func toProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus {
|
|||||||
BytesRx: peerState.BytesRx,
|
BytesRx: peerState.BytesRx,
|
||||||
BytesTx: peerState.BytesTx,
|
BytesTx: peerState.BytesTx,
|
||||||
RosenpassEnabled: peerState.RosenpassEnabled,
|
RosenpassEnabled: peerState.RosenpassEnabled,
|
||||||
Routes: maps.Keys(peerState.GetRoutes()),
|
Networks: maps.Keys(peerState.GetRoutes()),
|
||||||
Latency: durationpb.New(peerState.Latency),
|
Latency: durationpb.New(peerState.Latency),
|
||||||
}
|
}
|
||||||
pbFullStatus.Peers = append(pbFullStatus.Peers, pbPeerState)
|
pbFullStatus.Peers = append(pbFullStatus.Peers, pbPeerState)
|
||||||
|
@ -20,6 +20,8 @@ import (
|
|||||||
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"
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
|
"github.com/netbirdio/netbird/management/server/settings"
|
||||||
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
"github.com/netbirdio/netbird/signal/proto"
|
"github.com/netbirdio/netbird/signal/proto"
|
||||||
signalServer "github.com/netbirdio/netbird/signal/server"
|
signalServer "github.com/netbirdio/netbird/signal/server"
|
||||||
@ -110,7 +112,7 @@ func startManagement(t *testing.T, signalAddr string, counter *int) (*grpc.Serve
|
|||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
|
s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
|
||||||
store, cleanUp, err := server.NewTestStoreFromSQL(context.Background(), "", config.Datadir)
|
store, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "", config.Datadir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
@ -132,7 +134,7 @@ func startManagement(t *testing.T, signalAddr string, counter *int) (*grpc.Serve
|
|||||||
}
|
}
|
||||||
|
|
||||||
secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
|
secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
|
||||||
mgmtServer, err := server.NewServer(context.Background(), config, accountManager, peersUpdateManager, secretsManager, nil, nil)
|
mgmtServer, err := server.NewServer(context.Background(), config, accountManager, settings.NewManager(store), peersUpdateManager, secretsManager, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ func main() {
|
|||||||
var showSettings bool
|
var showSettings bool
|
||||||
flag.BoolVar(&showSettings, "settings", false, "run settings windows")
|
flag.BoolVar(&showSettings, "settings", false, "run settings windows")
|
||||||
var showRoutes bool
|
var showRoutes bool
|
||||||
flag.BoolVar(&showRoutes, "routes", false, "run routes windows")
|
flag.BoolVar(&showRoutes, "networks", false, "run networks windows")
|
||||||
var errorMSG string
|
var errorMSG string
|
||||||
flag.StringVar(&errorMSG, "error-msg", "", "displays a error message window")
|
flag.StringVar(&errorMSG, "error-msg", "", "displays a error message window")
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ func newServiceClient(addr string, a fyne.App, showSettings bool, showRoutes boo
|
|||||||
s.showSettingsUI()
|
s.showSettingsUI()
|
||||||
return s
|
return s
|
||||||
} else if showRoutes {
|
} else if showRoutes {
|
||||||
s.showRoutesUI()
|
s.showNetworksUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
@ -549,7 +549,7 @@ func (s *serviceClient) onTrayReady() {
|
|||||||
s.mAdvancedSettings = s.mSettings.AddSubMenuItem("Advanced Settings", "Advanced settings of the application")
|
s.mAdvancedSettings = s.mSettings.AddSubMenuItem("Advanced Settings", "Advanced settings of the application")
|
||||||
s.loadSettings()
|
s.loadSettings()
|
||||||
|
|
||||||
s.mRoutes = systray.AddMenuItem("Network Routes", "Open the routes management window")
|
s.mRoutes = systray.AddMenuItem("Networks", "Open the networks management window")
|
||||||
s.mRoutes.Disable()
|
s.mRoutes.Disable()
|
||||||
systray.AddSeparator()
|
systray.AddSeparator()
|
||||||
|
|
||||||
@ -657,7 +657,7 @@ func (s *serviceClient) onTrayReady() {
|
|||||||
s.mRoutes.Disable()
|
s.mRoutes.Disable()
|
||||||
go func() {
|
go func() {
|
||||||
defer s.mRoutes.Enable()
|
defer s.mRoutes.Enable()
|
||||||
s.runSelfCommand("routes", "true")
|
s.runSelfCommand("networks", "true")
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -19,32 +19,32 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
allRoutesText = "All routes"
|
allNetworksText = "All networks"
|
||||||
overlappingRoutesText = "Overlapping routes"
|
overlappingNetworksText = "Overlapping networks"
|
||||||
exitNodeRoutesText = "Exit-node routes"
|
exitNodeNetworksText = "Exit-node networks"
|
||||||
allRoutes filter = "all"
|
allNetworks filter = "all"
|
||||||
overlappingRoutes filter = "overlapping"
|
overlappingNetworks filter = "overlapping"
|
||||||
exitNodeRoutes filter = "exit-node"
|
exitNodeNetworks filter = "exit-node"
|
||||||
getClientFMT = "get client: %v"
|
getClientFMT = "get client: %v"
|
||||||
)
|
)
|
||||||
|
|
||||||
type filter string
|
type filter string
|
||||||
|
|
||||||
func (s *serviceClient) showRoutesUI() {
|
func (s *serviceClient) showNetworksUI() {
|
||||||
s.wRoutes = s.app.NewWindow("NetBird Routes")
|
s.wRoutes = s.app.NewWindow("Networks")
|
||||||
|
|
||||||
allGrid := container.New(layout.NewGridLayout(3))
|
allGrid := container.New(layout.NewGridLayout(3))
|
||||||
go s.updateRoutes(allGrid, allRoutes)
|
go s.updateNetworks(allGrid, allNetworks)
|
||||||
overlappingGrid := container.New(layout.NewGridLayout(3))
|
overlappingGrid := container.New(layout.NewGridLayout(3))
|
||||||
exitNodeGrid := container.New(layout.NewGridLayout(3))
|
exitNodeGrid := container.New(layout.NewGridLayout(3))
|
||||||
routeCheckContainer := container.NewVBox()
|
routeCheckContainer := container.NewVBox()
|
||||||
tabs := container.NewAppTabs(
|
tabs := container.NewAppTabs(
|
||||||
container.NewTabItem(allRoutesText, allGrid),
|
container.NewTabItem(allNetworksText, allGrid),
|
||||||
container.NewTabItem(overlappingRoutesText, overlappingGrid),
|
container.NewTabItem(overlappingNetworksText, overlappingGrid),
|
||||||
container.NewTabItem(exitNodeRoutesText, exitNodeGrid),
|
container.NewTabItem(exitNodeNetworksText, exitNodeGrid),
|
||||||
)
|
)
|
||||||
tabs.OnSelected = func(item *container.TabItem) {
|
tabs.OnSelected = func(item *container.TabItem) {
|
||||||
s.updateRoutesBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodeGrid)
|
s.updateNetworksBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodeGrid)
|
||||||
}
|
}
|
||||||
tabs.OnUnselected = func(item *container.TabItem) {
|
tabs.OnUnselected = func(item *container.TabItem) {
|
||||||
grid, _ := getGridAndFilterFromTab(tabs, allGrid, overlappingGrid, exitNodeGrid)
|
grid, _ := getGridAndFilterFromTab(tabs, allGrid, overlappingGrid, exitNodeGrid)
|
||||||
@ -58,17 +58,17 @@ func (s *serviceClient) showRoutesUI() {
|
|||||||
buttonBox := container.NewHBox(
|
buttonBox := container.NewHBox(
|
||||||
layout.NewSpacer(),
|
layout.NewSpacer(),
|
||||||
widget.NewButton("Refresh", func() {
|
widget.NewButton("Refresh", func() {
|
||||||
s.updateRoutesBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodeGrid)
|
s.updateNetworksBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodeGrid)
|
||||||
}),
|
}),
|
||||||
widget.NewButton("Select all", func() {
|
widget.NewButton("Select all", func() {
|
||||||
_, f := getGridAndFilterFromTab(tabs, allGrid, overlappingGrid, exitNodeGrid)
|
_, f := getGridAndFilterFromTab(tabs, allGrid, overlappingGrid, exitNodeGrid)
|
||||||
s.selectAllFilteredRoutes(f)
|
s.selectAllFilteredNetworks(f)
|
||||||
s.updateRoutesBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodeGrid)
|
s.updateNetworksBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodeGrid)
|
||||||
}),
|
}),
|
||||||
widget.NewButton("Deselect All", func() {
|
widget.NewButton("Deselect All", func() {
|
||||||
_, f := getGridAndFilterFromTab(tabs, allGrid, overlappingGrid, exitNodeGrid)
|
_, f := getGridAndFilterFromTab(tabs, allGrid, overlappingGrid, exitNodeGrid)
|
||||||
s.deselectAllFilteredRoutes(f)
|
s.deselectAllFilteredNetworks(f)
|
||||||
s.updateRoutesBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodeGrid)
|
s.updateNetworksBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodeGrid)
|
||||||
}),
|
}),
|
||||||
layout.NewSpacer(),
|
layout.NewSpacer(),
|
||||||
)
|
)
|
||||||
@ -81,36 +81,36 @@ func (s *serviceClient) showRoutesUI() {
|
|||||||
s.startAutoRefresh(10*time.Second, tabs, allGrid, overlappingGrid, exitNodeGrid)
|
s.startAutoRefresh(10*time.Second, tabs, allGrid, overlappingGrid, exitNodeGrid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceClient) updateRoutes(grid *fyne.Container, f filter) {
|
func (s *serviceClient) updateNetworks(grid *fyne.Container, f filter) {
|
||||||
grid.Objects = nil
|
grid.Objects = nil
|
||||||
grid.Refresh()
|
grid.Refresh()
|
||||||
idHeader := widget.NewLabelWithStyle(" ID", fyne.TextAlignLeading, fyne.TextStyle{Bold: true})
|
idHeader := widget.NewLabelWithStyle(" ID", fyne.TextAlignLeading, fyne.TextStyle{Bold: true})
|
||||||
networkHeader := widget.NewLabelWithStyle("Network/Domains", fyne.TextAlignLeading, fyne.TextStyle{Bold: true})
|
networkHeader := widget.NewLabelWithStyle("Range/Domains", fyne.TextAlignLeading, fyne.TextStyle{Bold: true})
|
||||||
resolvedIPsHeader := widget.NewLabelWithStyle("Resolved IPs", fyne.TextAlignLeading, fyne.TextStyle{Bold: true})
|
resolvedIPsHeader := widget.NewLabelWithStyle("Resolved IPs", fyne.TextAlignLeading, fyne.TextStyle{Bold: true})
|
||||||
|
|
||||||
grid.Add(idHeader)
|
grid.Add(idHeader)
|
||||||
grid.Add(networkHeader)
|
grid.Add(networkHeader)
|
||||||
grid.Add(resolvedIPsHeader)
|
grid.Add(resolvedIPsHeader)
|
||||||
|
|
||||||
filteredRoutes, err := s.getFilteredRoutes(f)
|
filteredRoutes, err := s.getFilteredNetworks(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sortRoutesByIDs(filteredRoutes)
|
sortNetworksByIDs(filteredRoutes)
|
||||||
|
|
||||||
for _, route := range filteredRoutes {
|
for _, route := range filteredRoutes {
|
||||||
r := route
|
r := route
|
||||||
|
|
||||||
checkBox := widget.NewCheck(r.GetID(), func(checked bool) {
|
checkBox := widget.NewCheck(r.GetID(), func(checked bool) {
|
||||||
s.selectRoute(r.ID, checked)
|
s.selectNetwork(r.ID, checked)
|
||||||
})
|
})
|
||||||
checkBox.Checked = route.Selected
|
checkBox.Checked = route.Selected
|
||||||
checkBox.Resize(fyne.NewSize(20, 20))
|
checkBox.Resize(fyne.NewSize(20, 20))
|
||||||
checkBox.Refresh()
|
checkBox.Refresh()
|
||||||
|
|
||||||
grid.Add(checkBox)
|
grid.Add(checkBox)
|
||||||
network := r.GetNetwork()
|
network := r.GetRange()
|
||||||
domains := r.GetDomains()
|
domains := r.GetDomains()
|
||||||
|
|
||||||
if len(domains) == 0 {
|
if len(domains) == 0 {
|
||||||
@ -129,10 +129,8 @@ func (s *serviceClient) updateRoutes(grid *fyne.Container, f filter) {
|
|||||||
grid.Add(domainsSelector)
|
grid.Add(domainsSelector)
|
||||||
|
|
||||||
var resolvedIPsList []string
|
var resolvedIPsList []string
|
||||||
for _, domain := range domains {
|
for domain, ipList := range r.GetResolvedIPs() {
|
||||||
if ipList, exists := r.GetResolvedIPs()[domain]; exists {
|
resolvedIPsList = append(resolvedIPsList, fmt.Sprintf("%s: %s", domain, strings.Join(ipList.GetIps(), ", ")))
|
||||||
resolvedIPsList = append(resolvedIPsList, fmt.Sprintf("%s: %s", domain, strings.Join(ipList.GetIps(), ", ")))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(resolvedIPsList) == 0 {
|
if len(resolvedIPsList) == 0 {
|
||||||
@ -151,35 +149,35 @@ func (s *serviceClient) updateRoutes(grid *fyne.Container, f filter) {
|
|||||||
grid.Refresh()
|
grid.Refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceClient) getFilteredRoutes(f filter) ([]*proto.Route, error) {
|
func (s *serviceClient) getFilteredNetworks(f filter) ([]*proto.Network, error) {
|
||||||
routes, err := s.fetchRoutes()
|
routes, err := s.fetchNetworks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(getClientFMT, err)
|
log.Errorf(getClientFMT, err)
|
||||||
s.showError(fmt.Errorf(getClientFMT, err))
|
s.showError(fmt.Errorf(getClientFMT, err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
switch f {
|
switch f {
|
||||||
case overlappingRoutes:
|
case overlappingNetworks:
|
||||||
return getOverlappingRoutes(routes), nil
|
return getOverlappingNetworks(routes), nil
|
||||||
case exitNodeRoutes:
|
case exitNodeNetworks:
|
||||||
return getExitNodeRoutes(routes), nil
|
return getExitNodeNetworks(routes), nil
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
return routes, nil
|
return routes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOverlappingRoutes(routes []*proto.Route) []*proto.Route {
|
func getOverlappingNetworks(routes []*proto.Network) []*proto.Network {
|
||||||
var filteredRoutes []*proto.Route
|
var filteredRoutes []*proto.Network
|
||||||
existingRange := make(map[string][]*proto.Route)
|
existingRange := make(map[string][]*proto.Network)
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
if len(route.Domains) > 0 {
|
if len(route.Domains) > 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if r, exists := existingRange[route.GetNetwork()]; exists {
|
if r, exists := existingRange[route.GetRange()]; exists {
|
||||||
r = append(r, route)
|
r = append(r, route)
|
||||||
existingRange[route.GetNetwork()] = r
|
existingRange[route.GetRange()] = r
|
||||||
} else {
|
} else {
|
||||||
existingRange[route.GetNetwork()] = []*proto.Route{route}
|
existingRange[route.GetRange()] = []*proto.Network{route}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, r := range existingRange {
|
for _, r := range existingRange {
|
||||||
@ -190,29 +188,29 @@ func getOverlappingRoutes(routes []*proto.Route) []*proto.Route {
|
|||||||
return filteredRoutes
|
return filteredRoutes
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExitNodeRoutes(routes []*proto.Route) []*proto.Route {
|
func getExitNodeNetworks(routes []*proto.Network) []*proto.Network {
|
||||||
var filteredRoutes []*proto.Route
|
var filteredRoutes []*proto.Network
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
if route.Network == "0.0.0.0/0" {
|
if route.Range == "0.0.0.0/0" {
|
||||||
filteredRoutes = append(filteredRoutes, route)
|
filteredRoutes = append(filteredRoutes, route)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return filteredRoutes
|
return filteredRoutes
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortRoutesByIDs(routes []*proto.Route) {
|
func sortNetworksByIDs(routes []*proto.Network) {
|
||||||
sort.Slice(routes, func(i, j int) bool {
|
sort.Slice(routes, func(i, j int) bool {
|
||||||
return strings.ToLower(routes[i].GetID()) < strings.ToLower(routes[j].GetID())
|
return strings.ToLower(routes[i].GetID()) < strings.ToLower(routes[j].GetID())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceClient) fetchRoutes() ([]*proto.Route, error) {
|
func (s *serviceClient) fetchNetworks() ([]*proto.Network, error) {
|
||||||
conn, err := s.getSrvClient(defaultFailTimeout)
|
conn, err := s.getSrvClient(defaultFailTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(getClientFMT, err)
|
return nil, fmt.Errorf(getClientFMT, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := conn.ListRoutes(s.ctx, &proto.ListRoutesRequest{})
|
resp, err := conn.ListNetworks(s.ctx, &proto.ListNetworksRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to list routes: %v", err)
|
return nil, fmt.Errorf("failed to list routes: %v", err)
|
||||||
}
|
}
|
||||||
@ -220,7 +218,7 @@ func (s *serviceClient) fetchRoutes() ([]*proto.Route, error) {
|
|||||||
return resp.Routes, nil
|
return resp.Routes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceClient) selectRoute(id string, checked bool) {
|
func (s *serviceClient) selectNetwork(id string, checked bool) {
|
||||||
conn, err := s.getSrvClient(defaultFailTimeout)
|
conn, err := s.getSrvClient(defaultFailTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(getClientFMT, err)
|
log.Errorf(getClientFMT, err)
|
||||||
@ -228,73 +226,73 @@ func (s *serviceClient) selectRoute(id string, checked bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req := &proto.SelectRoutesRequest{
|
req := &proto.SelectNetworksRequest{
|
||||||
RouteIDs: []string{id},
|
NetworkIDs: []string{id},
|
||||||
Append: checked,
|
Append: checked,
|
||||||
}
|
}
|
||||||
|
|
||||||
if checked {
|
if checked {
|
||||||
if _, err := conn.SelectRoutes(s.ctx, req); err != nil {
|
if _, err := conn.SelectNetworks(s.ctx, req); err != nil {
|
||||||
log.Errorf("failed to select route: %v", err)
|
log.Errorf("failed to select network: %v", err)
|
||||||
s.showError(fmt.Errorf("failed to select route: %v", err))
|
s.showError(fmt.Errorf("failed to select network: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Infof("Route %s selected", id)
|
log.Infof("Route %s selected", id)
|
||||||
} else {
|
} else {
|
||||||
if _, err := conn.DeselectRoutes(s.ctx, req); err != nil {
|
if _, err := conn.DeselectNetworks(s.ctx, req); err != nil {
|
||||||
log.Errorf("failed to deselect route: %v", err)
|
log.Errorf("failed to deselect network: %v", err)
|
||||||
s.showError(fmt.Errorf("failed to deselect route: %v", err))
|
s.showError(fmt.Errorf("failed to deselect network: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Infof("Route %s deselected", id)
|
log.Infof("Network %s deselected", id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceClient) selectAllFilteredRoutes(f filter) {
|
func (s *serviceClient) selectAllFilteredNetworks(f filter) {
|
||||||
conn, err := s.getSrvClient(defaultFailTimeout)
|
conn, err := s.getSrvClient(defaultFailTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(getClientFMT, err)
|
log.Errorf(getClientFMT, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req := s.getRoutesRequest(f, true)
|
req := s.getNetworksRequest(f, true)
|
||||||
if _, err := conn.SelectRoutes(s.ctx, req); err != nil {
|
if _, err := conn.SelectNetworks(s.ctx, req); err != nil {
|
||||||
log.Errorf("failed to select all routes: %v", err)
|
log.Errorf("failed to select all networks: %v", err)
|
||||||
s.showError(fmt.Errorf("failed to select all routes: %v", err))
|
s.showError(fmt.Errorf("failed to select all networks: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("All routes selected")
|
log.Debug("All networks selected")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceClient) deselectAllFilteredRoutes(f filter) {
|
func (s *serviceClient) deselectAllFilteredNetworks(f filter) {
|
||||||
conn, err := s.getSrvClient(defaultFailTimeout)
|
conn, err := s.getSrvClient(defaultFailTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(getClientFMT, err)
|
log.Errorf(getClientFMT, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req := s.getRoutesRequest(f, false)
|
req := s.getNetworksRequest(f, false)
|
||||||
if _, err := conn.DeselectRoutes(s.ctx, req); err != nil {
|
if _, err := conn.DeselectNetworks(s.ctx, req); err != nil {
|
||||||
log.Errorf("failed to deselect all routes: %v", err)
|
log.Errorf("failed to deselect all networks: %v", err)
|
||||||
s.showError(fmt.Errorf("failed to deselect all routes: %v", err))
|
s.showError(fmt.Errorf("failed to deselect all networks: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("All routes deselected")
|
log.Debug("All networks deselected")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceClient) getRoutesRequest(f filter, appendRoute bool) *proto.SelectRoutesRequest {
|
func (s *serviceClient) getNetworksRequest(f filter, appendRoute bool) *proto.SelectNetworksRequest {
|
||||||
req := &proto.SelectRoutesRequest{}
|
req := &proto.SelectNetworksRequest{}
|
||||||
if f == allRoutes {
|
if f == allNetworks {
|
||||||
req.All = true
|
req.All = true
|
||||||
} else {
|
} else {
|
||||||
routes, err := s.getFilteredRoutes(f)
|
routes, err := s.getFilteredNetworks(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
req.RouteIDs = append(req.RouteIDs, route.GetID())
|
req.NetworkIDs = append(req.NetworkIDs, route.GetID())
|
||||||
}
|
}
|
||||||
req.Append = appendRoute
|
req.Append = appendRoute
|
||||||
}
|
}
|
||||||
@ -311,7 +309,7 @@ func (s *serviceClient) startAutoRefresh(interval time.Duration, tabs *container
|
|||||||
ticker := time.NewTicker(interval)
|
ticker := time.NewTicker(interval)
|
||||||
go func() {
|
go func() {
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
s.updateRoutesBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodesGrid)
|
s.updateNetworksBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodesGrid)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -320,20 +318,20 @@ func (s *serviceClient) startAutoRefresh(interval time.Duration, tabs *container
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceClient) updateRoutesBasedOnDisplayTab(tabs *container.AppTabs, allGrid, overlappingGrid, exitNodesGrid *fyne.Container) {
|
func (s *serviceClient) updateNetworksBasedOnDisplayTab(tabs *container.AppTabs, allGrid, overlappingGrid, exitNodesGrid *fyne.Container) {
|
||||||
grid, f := getGridAndFilterFromTab(tabs, allGrid, overlappingGrid, exitNodesGrid)
|
grid, f := getGridAndFilterFromTab(tabs, allGrid, overlappingGrid, exitNodesGrid)
|
||||||
s.wRoutes.Content().Refresh()
|
s.wRoutes.Content().Refresh()
|
||||||
s.updateRoutes(grid, f)
|
s.updateNetworks(grid, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGridAndFilterFromTab(tabs *container.AppTabs, allGrid, overlappingGrid, exitNodesGrid *fyne.Container) (*fyne.Container, filter) {
|
func getGridAndFilterFromTab(tabs *container.AppTabs, allGrid, overlappingGrid, exitNodesGrid *fyne.Container) (*fyne.Container, filter) {
|
||||||
switch tabs.Selected().Text {
|
switch tabs.Selected().Text {
|
||||||
case overlappingRoutesText:
|
case overlappingNetworksText:
|
||||||
return overlappingGrid, overlappingRoutes
|
return overlappingGrid, overlappingNetworks
|
||||||
case exitNodeRoutesText:
|
case exitNodeNetworksText:
|
||||||
return exitNodesGrid, exitNodeRoutes
|
return exitNodesGrid, exitNodeNetworks
|
||||||
default:
|
default:
|
||||||
return allGrid, allRoutes
|
return allGrid, allNetworks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,3 +108,9 @@ func GetParsedDomainLabel(name string) (string, error) {
|
|||||||
|
|
||||||
return validHost, nil
|
return validHost, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NormalizeZone returns a normalized domain name without the wildcard prefix
|
||||||
|
func NormalizeZone(domain string) string {
|
||||||
|
d, _ := strings.CutPrefix(domain, "*.")
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
3
go.mod
3
go.mod
@ -60,7 +60,7 @@ require (
|
|||||||
github.com/miekg/dns v1.1.59
|
github.com/miekg/dns v1.1.59
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
github.com/nadoo/ipset v0.5.0
|
github.com/nadoo/ipset v0.5.0
|
||||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20241106153857-de8e2beb5254
|
github.com/netbirdio/management-integrations/integrations v0.0.0-20241211172827-ba0a446be480
|
||||||
github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d
|
github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d
|
||||||
github.com/okta/okta-sdk-golang/v2 v2.18.0
|
github.com/okta/okta-sdk-golang/v2 v2.18.0
|
||||||
github.com/oschwald/maxminddb-golang v1.12.0
|
github.com/oschwald/maxminddb-golang v1.12.0
|
||||||
@ -212,6 +212,7 @@ require (
|
|||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/spf13/cast v1.5.0 // indirect
|
||||||
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
|
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
|
||||||
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
|
||||||
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||||
github.com/tklauser/numcpus v0.8.0 // indirect
|
github.com/tklauser/numcpus v0.8.0 // indirect
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
|
5
go.sum
5
go.sum
@ -528,8 +528,8 @@ github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944 h1:TDtJKmM6S
|
|||||||
github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944/go.mod h1:sHA6TRxjQ6RLbnI+3R4DZo2Eseg/iKiPRfNmcuNySVQ=
|
github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944/go.mod h1:sHA6TRxjQ6RLbnI+3R4DZo2Eseg/iKiPRfNmcuNySVQ=
|
||||||
github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e h1:PURA50S8u4mF6RrkYYCAvvPCixhqqEiEy3Ej6avh04c=
|
github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e h1:PURA50S8u4mF6RrkYYCAvvPCixhqqEiEy3Ej6avh04c=
|
||||||
github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e/go.mod h1:YMLU7qbKfVjmEv7EoZPIVEI+kNYxWCdPK3VS0BU+U4Q=
|
github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e/go.mod h1:YMLU7qbKfVjmEv7EoZPIVEI+kNYxWCdPK3VS0BU+U4Q=
|
||||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20241106153857-de8e2beb5254 h1:L8mNd3tBxMdnQNxMNJ+/EiwHwizNOMy8/nHLVGNfjpg=
|
github.com/netbirdio/management-integrations/integrations v0.0.0-20241211172827-ba0a446be480 h1:M+UPn/o+plVE7ZehgL6/1dftptsO1tyTPssgImgi+28=
|
||||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20241106153857-de8e2beb5254/go.mod h1:nykwWZnxb+sJz2Z//CEq45CMRWSHllH8pODKRB8eY7Y=
|
github.com/netbirdio/management-integrations/integrations v0.0.0-20241211172827-ba0a446be480/go.mod h1:RC0PnyATSBPrRWKQgb+7KcC1tMta9eYyzuA414RG9wQ=
|
||||||
github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502 h1:3tHlFmhTdX9axERMVN63dqyFqnvuD+EMJHzM7mNGON8=
|
github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502 h1:3tHlFmhTdX9axERMVN63dqyFqnvuD+EMJHzM7mNGON8=
|
||||||
github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
||||||
github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d h1:bRq5TKgC7Iq20pDiuC54yXaWnAVeS5PdGpSokFTlR28=
|
github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d h1:bRq5TKgC7Iq20pDiuC54yXaWnAVeS5PdGpSokFTlR28=
|
||||||
@ -669,6 +669,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
@ -11,6 +11,8 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
|
"github.com/netbirdio/netbird/management/server/settings"
|
||||||
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/system"
|
"github.com/netbirdio/netbird/client/system"
|
||||||
@ -57,7 +59,7 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
s := grpc.NewServer()
|
s := grpc.NewServer()
|
||||||
store, cleanUp, err := mgmt.NewTestStoreFromSQL(context.Background(), "../server/testdata/store.sql", t.TempDir())
|
store, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "../server/testdata/store.sql", t.TempDir())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -76,7 +78,7 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
secretsManager := mgmt.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
|
secretsManager := mgmt.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
|
||||||
mgmtServer, err := mgmt.NewServer(context.Background(), config, accountManager, peersUpdateManager, secretsManager, nil, nil)
|
mgmtServer, err := mgmt.NewServer(context.Background(), config, accountManager, settings.NewManager(store), peersUpdateManager, secretsManager, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -41,12 +41,20 @@ import (
|
|||||||
"github.com/netbirdio/netbird/management/server"
|
"github.com/netbirdio/netbird/management/server"
|
||||||
nbContext "github.com/netbirdio/netbird/management/server/context"
|
nbContext "github.com/netbirdio/netbird/management/server/context"
|
||||||
"github.com/netbirdio/netbird/management/server/geolocation"
|
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||||
|
"github.com/netbirdio/netbird/management/server/groups"
|
||||||
httpapi "github.com/netbirdio/netbird/management/server/http"
|
httpapi "github.com/netbirdio/netbird/management/server/http"
|
||||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||||
"github.com/netbirdio/netbird/management/server/idp"
|
"github.com/netbirdio/netbird/management/server/idp"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
"github.com/netbirdio/netbird/management/server/metrics"
|
"github.com/netbirdio/netbird/management/server/metrics"
|
||||||
|
"github.com/netbirdio/netbird/management/server/networks"
|
||||||
|
"github.com/netbirdio/netbird/management/server/networks/resources"
|
||||||
|
"github.com/netbirdio/netbird/management/server/networks/routers"
|
||||||
|
"github.com/netbirdio/netbird/management/server/permissions"
|
||||||
|
"github.com/netbirdio/netbird/management/server/settings"
|
||||||
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
|
"github.com/netbirdio/netbird/management/server/users"
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
"github.com/netbirdio/netbird/version"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
@ -150,7 +158,7 @@ var (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
store, err := server.NewStore(ctx, config.StoreConfig.Engine, config.Datadir, appMetrics)
|
store, err := store.NewStore(ctx, config.StoreConfig.Engine, config.Datadir, appMetrics)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed creating Store: %s: %v", config.Datadir, err)
|
return fmt.Errorf("failed creating Store: %s: %v", config.Datadir, err)
|
||||||
}
|
}
|
||||||
@ -265,7 +273,15 @@ var (
|
|||||||
KeysLocation: config.HttpConfig.AuthKeysLocation,
|
KeysLocation: config.HttpConfig.AuthKeysLocation,
|
||||||
}
|
}
|
||||||
|
|
||||||
httpAPIHandler, err := httpapi.APIHandler(ctx, accountManager, geo, *jwtValidator, appMetrics, httpAPIAuthCfg, integratedPeerValidator)
|
userManager := users.NewManager(store)
|
||||||
|
settingsManager := settings.NewManager(store)
|
||||||
|
permissionsManager := permissions.NewManager(userManager, settingsManager)
|
||||||
|
groupsManager := groups.NewManager(store, permissionsManager, accountManager)
|
||||||
|
resourcesManager := resources.NewManager(store, permissionsManager, groupsManager, accountManager)
|
||||||
|
routersManager := routers.NewManager(store, permissionsManager, accountManager)
|
||||||
|
networksManager := networks.NewManager(store, permissionsManager, resourcesManager, routersManager, accountManager)
|
||||||
|
|
||||||
|
httpAPIHandler, err := httpapi.APIHandler(ctx, accountManager, networksManager, resourcesManager, routersManager, groupsManager, geo, *jwtValidator, appMetrics, httpAPIAuthCfg, integratedPeerValidator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed creating HTTP API handler: %v", err)
|
return fmt.Errorf("failed creating HTTP API handler: %v", err)
|
||||||
}
|
}
|
||||||
@ -274,7 +290,7 @@ var (
|
|||||||
ephemeralManager.LoadInitialPeers(ctx)
|
ephemeralManager.LoadInitialPeers(ctx)
|
||||||
|
|
||||||
gRPCAPIHandler := grpc.NewServer(gRPCOpts...)
|
gRPCAPIHandler := grpc.NewServer(gRPCOpts...)
|
||||||
srv, err := server.NewServer(ctx, config, accountManager, peersUpdateManager, secretsManager, appMetrics, ephemeralManager)
|
srv, err := server.NewServer(ctx, config, accountManager, settingsManager, peersUpdateManager, secretsManager, appMetrics, ephemeralManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed creating gRPC API handler: %v", err)
|
return fmt.Errorf("failed creating gRPC API handler: %v", err)
|
||||||
}
|
}
|
||||||
@ -400,7 +416,7 @@ func notifyStop(ctx context.Context, msg string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getInstallationID(ctx context.Context, store server.Store) (string, error) {
|
func getInstallationID(ctx context.Context, store store.Store) (string, error) {
|
||||||
installationID := store.GetInstallationID()
|
installationID := store.GetInstallationID()
|
||||||
if installationID != "" {
|
if installationID != "" {
|
||||||
return installationID, nil
|
return installationID, nil
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/formatter"
|
"github.com/netbirdio/netbird/formatter"
|
||||||
"github.com/netbirdio/netbird/management/server"
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ var upCmd = &cobra.Command{
|
|||||||
//nolint
|
//nolint
|
||||||
ctx := context.WithValue(cmd.Context(), formatter.ExecutionContextKey, formatter.SystemSource)
|
ctx := context.WithValue(cmd.Context(), formatter.ExecutionContextKey, formatter.SystemSource)
|
||||||
|
|
||||||
if err := server.MigrateFileStoreToSqlite(ctx, mgmtDataDir); err != nil {
|
if err := store.MigrateFileStoreToSqlite(ctx, mgmtDataDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.WithContext(ctx).Info("Migration finished successfully")
|
log.WithContext(ctx).Info("Migration finished successfully")
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.26.0
|
// protoc-gen-go v1.26.0
|
||||||
// protoc v4.23.4
|
// protoc v4.24.3
|
||||||
// source: management.proto
|
// source: management.proto
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
@ -29,6 +29,7 @@ const (
|
|||||||
RuleProtocol_TCP RuleProtocol = 2
|
RuleProtocol_TCP RuleProtocol = 2
|
||||||
RuleProtocol_UDP RuleProtocol = 3
|
RuleProtocol_UDP RuleProtocol = 3
|
||||||
RuleProtocol_ICMP RuleProtocol = 4
|
RuleProtocol_ICMP RuleProtocol = 4
|
||||||
|
RuleProtocol_CUSTOM RuleProtocol = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enum value maps for RuleProtocol.
|
// Enum value maps for RuleProtocol.
|
||||||
@ -39,6 +40,7 @@ var (
|
|||||||
2: "TCP",
|
2: "TCP",
|
||||||
3: "UDP",
|
3: "UDP",
|
||||||
4: "ICMP",
|
4: "ICMP",
|
||||||
|
5: "CUSTOM",
|
||||||
}
|
}
|
||||||
RuleProtocol_value = map[string]int32{
|
RuleProtocol_value = map[string]int32{
|
||||||
"UNKNOWN": 0,
|
"UNKNOWN": 0,
|
||||||
@ -46,6 +48,7 @@ var (
|
|||||||
"TCP": 2,
|
"TCP": 2,
|
||||||
"UDP": 3,
|
"UDP": 3,
|
||||||
"ICMP": 4,
|
"ICMP": 4,
|
||||||
|
"CUSTOM": 5,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1393,7 +1396,8 @@ type PeerConfig struct {
|
|||||||
// SSHConfig of the peer.
|
// SSHConfig of the peer.
|
||||||
SshConfig *SSHConfig `protobuf:"bytes,3,opt,name=sshConfig,proto3" json:"sshConfig,omitempty"`
|
SshConfig *SSHConfig `protobuf:"bytes,3,opt,name=sshConfig,proto3" json:"sshConfig,omitempty"`
|
||||||
// Peer fully qualified domain name
|
// Peer fully qualified domain name
|
||||||
Fqdn string `protobuf:"bytes,4,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
|
Fqdn string `protobuf:"bytes,4,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
|
||||||
|
RoutingPeerDnsResolutionEnabled bool `protobuf:"varint,5,opt,name=RoutingPeerDnsResolutionEnabled,proto3" json:"RoutingPeerDnsResolutionEnabled,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PeerConfig) Reset() {
|
func (x *PeerConfig) Reset() {
|
||||||
@ -1456,6 +1460,13 @@ func (x *PeerConfig) GetFqdn() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *PeerConfig) GetRoutingPeerDnsResolutionEnabled() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.RoutingPeerDnsResolutionEnabled
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// NetworkMap represents a network state of the peer with the corresponding configuration parameters to establish peer-to-peer connections
|
// NetworkMap represents a network state of the peer with the corresponding configuration parameters to establish peer-to-peer connections
|
||||||
type NetworkMap struct {
|
type NetworkMap struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
@ -2780,6 +2791,10 @@ type RouteFirewallRule struct {
|
|||||||
PortInfo *PortInfo `protobuf:"bytes,5,opt,name=portInfo,proto3" json:"portInfo,omitempty"`
|
PortInfo *PortInfo `protobuf:"bytes,5,opt,name=portInfo,proto3" json:"portInfo,omitempty"`
|
||||||
// IsDynamic indicates if the route is a DNS route.
|
// IsDynamic indicates if the route is a DNS route.
|
||||||
IsDynamic bool `protobuf:"varint,6,opt,name=isDynamic,proto3" json:"isDynamic,omitempty"`
|
IsDynamic bool `protobuf:"varint,6,opt,name=isDynamic,proto3" json:"isDynamic,omitempty"`
|
||||||
|
// Domains is a list of domains for which the rule is applicable.
|
||||||
|
Domains []string `protobuf:"bytes,7,rep,name=domains,proto3" json:"domains,omitempty"`
|
||||||
|
// CustomProtocol is a custom protocol ID.
|
||||||
|
CustomProtocol uint32 `protobuf:"varint,8,opt,name=customProtocol,proto3" json:"customProtocol,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RouteFirewallRule) Reset() {
|
func (x *RouteFirewallRule) Reset() {
|
||||||
@ -2856,6 +2871,20 @@ func (x *RouteFirewallRule) GetIsDynamic() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *RouteFirewallRule) GetDomains() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Domains
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RouteFirewallRule) GetCustomProtocol() uint32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.CustomProtocol
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type PortInfo_Range struct {
|
type PortInfo_Range struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@ -3075,7 +3104,7 @@ var file_management_proto_rawDesc = []byte{
|
|||||||
0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18,
|
0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18,
|
||||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x70,
|
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x70,
|
||||||
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
|
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
|
||||||
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x81, 0x01, 0x0a, 0x0a, 0x50, 0x65, 0x65, 0x72,
|
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0xcb, 0x01, 0x0a, 0x0a, 0x50, 0x65, 0x65, 0x72,
|
||||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||||
0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||||
0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64,
|
0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64,
|
||||||
@ -3083,250 +3112,260 @@ var file_management_proto_rawDesc = []byte{
|
|||||||
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
0x03, 0x20, 0x01, 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,
|
0x6e, 0x74, 0x2e, 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,
|
0x68, 0x43, 0x6f, 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, 0xf3, 0x04, 0x0a, 0x0a,
|
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x12, 0x48, 0x0a, 0x1f, 0x52,
|
||||||
0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65,
|
0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x65, 0x72, 0x44, 0x6e, 0x73, 0x52, 0x65, 0x73,
|
||||||
0x72, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x72, 0x69,
|
0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05,
|
||||||
0x61, 0x6c, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x65,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
0x72, 0x44, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e,
|
||||||
0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a,
|
0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xf3, 0x04, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
|
||||||
0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x0b, 0x72, 0x65,
|
0x6b, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x01,
|
||||||
0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x0a,
|
||||||
0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d,
|
0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
|
||||||
0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x72,
|
0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65,
|
||||||
0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65,
|
0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f,
|
||||||
0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
0x6e, 0x66, 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65,
|
||||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65,
|
0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
|
||||||
0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x06, 0x52, 0x6f,
|
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65,
|
||||||
0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x61, 0x6e,
|
0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50,
|
||||||
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, 0x52,
|
0x65, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65,
|
||||||
0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66,
|
0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
|
||||||
0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45,
|
||||||
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
|
0x6d, 0x70, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05,
|
||||||
0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x40, 0x0a, 0x0c, 0x6f, 0x66,
|
0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
|
||||||
0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b,
|
0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12,
|
||||||
0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65,
|
0x33, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01,
|
||||||
0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c,
|
0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
||||||
0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x3e, 0x0a, 0x0d,
|
0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f,
|
||||||
0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x08, 0x20,
|
0x6e, 0x66, 0x69, 0x67, 0x12, 0x40, 0x0a, 0x0c, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50,
|
||||||
0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
0x65, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e,
|
||||||
0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x46,
|
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65,
|
||||||
0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x14,
|
0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e,
|
||||||
0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45,
|
0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x3e, 0x0a, 0x0d, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61,
|
||||||
0x6d, 0x70, 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x66, 0x69, 0x72, 0x65,
|
0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e,
|
||||||
0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77,
|
||||||
0x12, 0x4f, 0x0a, 0x13, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61,
|
0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c,
|
||||||
0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e,
|
0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61,
|
||||||
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65,
|
0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x09,
|
||||||
0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x13, 0x72, 0x6f,
|
0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75,
|
||||||
|
0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4f, 0x0a, 0x13, 0x72, 0x6f,
|
||||||
0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65,
|
0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65,
|
||||||
0x73, 0x12, 0x3e, 0x0a, 0x1a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77,
|
0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
||||||
0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18,
|
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61,
|
||||||
0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72,
|
0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x13, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69,
|
||||||
0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74,
|
0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x1a, 0x72,
|
||||||
0x79, 0x22, 0x97, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72,
|
0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c,
|
||||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b,
|
0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b,
|
0x1a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52,
|
||||||
0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73,
|
0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x97, 0x01, 0x0a, 0x10,
|
||||||
0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49,
|
0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
0x70, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18,
|
0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a,
|
||||||
0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73,
|
0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
|
||||||
0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18,
|
0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x33, 0x0a, 0x09,
|
||||||
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x22, 0x49, 0x0a, 0x09, 0x53,
|
0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||||
0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x73, 0x68, 0x45,
|
0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48,
|
||||||
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x73,
|
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50,
|
0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68,
|
0x04, 0x66, 0x71, 0x64, 0x6e, 0x22, 0x49, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x20, 0x0a, 0x1e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65,
|
0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
|
||||||
0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c,
|
||||||
0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbf, 0x01, 0x0a, 0x17, 0x44, 0x65, 0x76,
|
0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18,
|
||||||
0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79,
|
||||||
0x46, 0x6c, 0x6f, 0x77, 0x12, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
|
0x22, 0x20, 0x0a, 0x1e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x65, 0x6e, 0x74, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
|
0x73, 0x74, 0x22, 0xbf, 0x01, 0x0a, 0x17, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74,
|
||||||
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x48,
|
||||||
0x69, 0x64, 0x65, 0x72, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x42,
|
0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
|
||||||
0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
0x32, 0x2c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x65,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
|
||||||
0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66,
|
0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x08,
|
||||||
0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66,
|
0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76,
|
||||||
0x69, 0x67, 0x22, 0x16, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0a,
|
0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
|
||||||
0x0a, 0x06, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x00, 0x22, 0x1e, 0x0a, 0x1c, 0x50, 0x4b,
|
0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72,
|
||||||
0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46,
|
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72,
|
||||||
0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5b, 0x0a, 0x15, 0x50, 0x4b,
|
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x16, 0x0a, 0x08,
|
||||||
0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46,
|
0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x48, 0x4f, 0x53, 0x54,
|
||||||
0x6c, 0x6f, 0x77, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43,
|
0x45, 0x44, 0x10, 0x00, 0x22, 0x1e, 0x0a, 0x1c, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68,
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61,
|
0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71,
|
||||||
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65,
|
0x75, 0x65, 0x73, 0x74, 0x22, 0x5b, 0x0a, 0x15, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68,
|
||||||
0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65,
|
0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x42, 0x0a,
|
||||||
0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xea, 0x02, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76,
|
0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18,
|
||||||
0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6c,
|
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
||||||
0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6c,
|
0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74,
|
0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, 0x6c,
|
0x67, 0x22, 0xea, 0x02, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f,
|
||||||
0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f,
|
0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44,
|
||||||
0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44,
|
||||||
0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04,
|
0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2e,
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65,
|
||||||
0x0a, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x64, 0x70,
|
0x63, 0x72, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03,
|
||||||
0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x44, 0x65, 0x76, 0x69,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08,
|
||||||
0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x24,
|
0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||||
0x0a, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18,
|
0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x44, 0x65, 0x76, 0x69,
|
||||||
0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70,
|
0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05,
|
||||||
0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x07, 0x20,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x55, 0x73,
|
0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x6f, 0x6b, 0x65,
|
||||||
0x65, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a,
|
0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x55, 0x73, 0x65, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x34, 0x0a, 0x15, 0x41, 0x75,
|
0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14,
|
||||||
0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f,
|
0x0a, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53,
|
||||||
0x69, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f,
|
0x63, 0x6f, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x49, 0x44, 0x54, 0x6f, 0x6b,
|
||||||
0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x55, 0x73, 0x65, 0x49, 0x44, 0x54,
|
||||||
0x12, 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, 0x73,
|
0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x34, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a,
|
||||||
0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
|
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x09, 0x20,
|
||||||
0x55, 0x52, 0x4c, 0x73, 0x22, 0xed, 0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x0e,
|
0x01, 0x28, 0x09, 0x52, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69,
|
||||||
0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18,
|
0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x52, 0x65,
|
||||||
0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09,
|
||||||
0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x74, 0x77,
|
0x52, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, 0x73, 0x22, 0xed,
|
||||||
0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x4e,
|
0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01,
|
||||||
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x65,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77,
|
||||||
0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, 0x16,
|
0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f,
|
||||||
0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06,
|
0x72, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70,
|
||||||
0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, 0x65,
|
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
|
||||||
0x72, 0x61, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x4d, 0x61, 0x73, 0x71,
|
0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01,
|
||||||
0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, 0x18,
|
0x28, 0x09, 0x52, 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72,
|
||||||
0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07,
|
0x69, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
|
||||||
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44,
|
0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x06,
|
||||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f,
|
0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65,
|
||||||
0x75, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52,
|
0x12, 0x14, 0x0a, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x6f, 0x75, 0x74, 0x65, 0x22, 0xb4, 0x01, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66,
|
0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||||
0x69, 0x67, 0x12, 0x24, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x61,
|
0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73,
|
||||||
0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69,
|
0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x09, 0x20,
|
||||||
0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65,
|
0x01, 0x28, 0x08, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x22, 0xb4,
|
||||||
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03,
|
0x01, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24, 0x0a, 0x0d,
|
||||||
0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20,
|
||||||
0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52,
|
0x01, 0x28, 0x08, 0x52, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62,
|
||||||
0x10, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70,
|
0x6c, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
||||||
0x73, 0x12, 0x38, 0x0a, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x73,
|
0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d,
|
||||||
0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65,
|
||||||
0x65, 0x6e, 0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x0b,
|
0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x53,
|
||||||
0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x22, 0x58, 0x0a, 0x0a, 0x43,
|
0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x43,
|
||||||
0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d,
|
0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
|
||||||
0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x75,
|
||||||
0x6e, 0x12, 0x32, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03,
|
0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d,
|
||||||
0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x22, 0x58, 0x0a, 0x0a, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a,
|
||||||
0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x52, 0x65,
|
0x6f, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20,
|
||||||
0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x74, 0x0a, 0x0c, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52,
|
0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x32, 0x0a, 0x07, 0x52,
|
||||||
0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
|
0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70,
|
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65,
|
||||||
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a,
|
0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22,
|
||||||
0x05, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x43, 0x6c,
|
0x74, 0x0a, 0x0c, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12,
|
||||||
0x61, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03,
|
0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e,
|
||||||
0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x14, 0x0a, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x18, 0x05,
|
0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x22, 0xb3, 0x01, 0x0a, 0x0f,
|
0x03, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x43, 0x6c, 0x61, 0x73, 0x73,
|
||||||
0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12,
|
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x10, 0x0a,
|
||||||
0x38, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01,
|
0x03, 0x54, 0x54, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12,
|
||||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
|
0x14, 0x0a, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
||||||
0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0b, 0x4e, 0x61,
|
0x52, 0x44, 0x61, 0x74, 0x61, 0x22, 0xb3, 0x01, 0x0a, 0x0f, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65,
|
||||||
0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x72, 0x69,
|
0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x38, 0x0a, 0x0b, 0x4e, 0x61, 0x6d,
|
||||||
0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x50, 0x72, 0x69, 0x6d,
|
0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16,
|
||||||
0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x03,
|
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65,
|
||||||
0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x32, 0x0a,
|
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76,
|
||||||
0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e,
|
0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02,
|
||||||
0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x53, 0x65, 0x61,
|
0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a,
|
||||||
0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65,
|
0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07,
|
||||||
0x64, 0x22, 0x48, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12,
|
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x53, 0x65, 0x61, 0x72, 0x63,
|
||||||
0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12,
|
0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18,
|
||||||
0x16, 0x0a, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
|
0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d,
|
||||||
0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18,
|
0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x48, 0x0a, 0x0a, 0x4e,
|
||||||
0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0xd9, 0x01, 0x0a, 0x0c,
|
0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18,
|
||||||
0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06,
|
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x4e, 0x53, 0x54,
|
||||||
0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x50, 0x65,
|
0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70,
|
||||||
0x65, 0x72, 0x49, 0x50, 0x12, 0x37, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f,
|
0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52,
|
||||||
0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0xd9, 0x01, 0x0a, 0x0c, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61,
|
||||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69,
|
0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50,
|
||||||
0x6f, 0x6e, 0x52, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x12, 0x37,
|
||||||
0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e,
|
0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41,
|
0x0e, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52,
|
||||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a,
|
0x75, 0x6c, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x44, 0x69,
|
||||||
0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32,
|
0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f,
|
||||||
0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c,
|
0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
||||||
0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f,
|
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52,
|
||||||
0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28,
|
0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
0x09, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x38, 0x0a, 0x0e, 0x4e, 0x65, 0x74, 0x77, 0x6f,
|
0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
|
||||||
0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x65, 0x74,
|
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x50, 0x12,
|
0x63, 0x6f, 0x6c, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a,
|
||||||
0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61,
|
0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x6f, 0x72,
|
||||||
0x63, 0x22, 0x1e, 0x0a, 0x06, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x46,
|
0x74, 0x22, 0x38, 0x0a, 0x0e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72,
|
||||||
0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x46, 0x69, 0x6c, 0x65,
|
0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x73, 0x22, 0x96, 0x01, 0x0a, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14,
|
0x28, 0x09, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x50, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63,
|
||||||
0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x04,
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x63, 0x22, 0x1e, 0x0a, 0x06, 0x43,
|
||||||
0x70, 0x6f, 0x72, 0x74, 0x12, 0x32, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20,
|
0x68, 0x65, 0x63, 0x6b, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01,
|
||||||
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x96, 0x01, 0x0a, 0x08,
|
||||||
0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48,
|
0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74,
|
||||||
0x00, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0x2f, 0x0a, 0x05, 0x52, 0x61, 0x6e, 0x67,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x32,
|
||||||
0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
|
0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
|
||||||
0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02,
|
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49,
|
||||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x70, 0x6f, 0x72,
|
0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x00, 0x52, 0x05, 0x72, 0x61, 0x6e,
|
||||||
0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x8f, 0x02, 0x0a, 0x11, 0x52,
|
0x67, 0x65, 0x1a, 0x2f, 0x0a, 0x05, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73,
|
||||||
0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65,
|
0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72,
|
||||||
0x12, 0x22, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73,
|
0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03,
|
||||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61,
|
0x65, 0x6e, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63,
|
||||||
0x6e, 0x67, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02,
|
0x74, 0x69, 0x6f, 0x6e, 0x22, 0xd1, 0x02, 0x0a, 0x11, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69,
|
||||||
0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
|
0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x6f,
|
||||||
0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63,
|
0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09,
|
||||||
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
|
0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x2e,
|
||||||
0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69,
|
0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16,
|
||||||
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
|
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65,
|
||||||
0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20,
|
||||||
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
|
0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20,
|
||||||
0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x30, 0x0a, 0x08,
|
0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||||
0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14,
|
0x12, 0x34, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01,
|
||||||
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74,
|
0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
||||||
0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c,
|
0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72,
|
||||||
0x0a, 0x09, 0x69, 0x73, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28,
|
0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x30, 0x0a, 0x08, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e,
|
||||||
0x08, 0x52, 0x09, 0x69, 0x73, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x2a, 0x40, 0x0a, 0x0c,
|
0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
||||||
0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0b, 0x0a, 0x07,
|
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08,
|
||||||
0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4c, 0x4c,
|
0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x73, 0x44, 0x79,
|
||||||
0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x55,
|
0x6e, 0x61, 0x6d, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x44,
|
||||||
0x44, 0x50, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x43, 0x4d, 0x50, 0x10, 0x04, 0x2a, 0x20,
|
0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||||
0x0a, 0x0d, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
|
0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73,
|
||||||
0x06, 0x0a, 0x02, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x55, 0x54, 0x10, 0x01,
|
0x12, 0x26, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
|
||||||
0x2a, 0x22, 0x0a, 0x0a, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a,
|
0x6f, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d,
|
||||||
0x0a, 0x06, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x52,
|
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2a, 0x4c, 0x0a, 0x0c, 0x52, 0x75, 0x6c, 0x65,
|
||||||
0x4f, 0x50, 0x10, 0x01, 0x32, 0x90, 0x04, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e,
|
||||||
0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f,
|
0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x07,
|
||||||
0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x03,
|
||||||
0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
|
0x12, 0x08, 0x0a, 0x04, 0x49, 0x43, 0x4d, 0x50, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55,
|
||||||
0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45,
|
0x53, 0x54, 0x4f, 0x4d, 0x10, 0x05, 0x2a, 0x20, 0x0a, 0x0d, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69,
|
||||||
0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
|
0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x06, 0x0a, 0x02, 0x49, 0x4e, 0x10, 0x00, 0x12,
|
||||||
0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
|
0x07, 0x0a, 0x03, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x2a, 0x22, 0x0a, 0x0a, 0x52, 0x75, 0x6c, 0x65,
|
||||||
|
0x41, 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, 0x32, 0x90, 0x04, 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,
|
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,
|
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e,
|
||||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65,
|
0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45,
|
||||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74,
|
0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a,
|
||||||
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
|
0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63,
|
||||||
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d,
|
0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30,
|
||||||
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65,
|
||||||
0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a,
|
0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45,
|
||||||
0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e,
|
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
|
||||||
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e,
|
0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74,
|
||||||
0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41,
|
0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
||||||
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77,
|
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
||||||
0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e,
|
0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65,
|
||||||
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c,
|
0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
|
||||||
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72,
|
0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
||||||
0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x58,
|
|
||||||
0x0a, 0x18, 0x47, 0x65, 0x74, 0x50, 0x4b, 0x43, 0x45, 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, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d,
|
||||||
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x08, 0x53, 0x79, 0x6e, 0x63,
|
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
||||||
0x4d, 0x65, 0x74, 0x61, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
|
0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73,
|
||||||
0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61,
|
0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x50, 0x4b, 0x43,
|
||||||
0x67, 0x65, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c,
|
||||||
0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74,
|
0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
||||||
0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
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, 0x3d, 0x0a, 0x08, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x74, 0x61, 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, 0x11, 0x2e, 0x6d, 0x61, 0x6e,
|
||||||
|
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42,
|
||||||
|
0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
|
0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -222,6 +222,8 @@ message PeerConfig {
|
|||||||
SSHConfig sshConfig = 3;
|
SSHConfig sshConfig = 3;
|
||||||
// Peer fully qualified domain name
|
// Peer fully qualified domain name
|
||||||
string fqdn = 4;
|
string fqdn = 4;
|
||||||
|
|
||||||
|
bool RoutingPeerDnsResolutionEnabled = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkMap represents a network state of the peer with the corresponding configuration parameters to establish peer-to-peer connections
|
// NetworkMap represents a network state of the peer with the corresponding configuration parameters to establish peer-to-peer connections
|
||||||
@ -396,6 +398,7 @@ enum RuleProtocol {
|
|||||||
TCP = 2;
|
TCP = 2;
|
||||||
UDP = 3;
|
UDP = 3;
|
||||||
ICMP = 4;
|
ICMP = 4;
|
||||||
|
CUSTOM = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RuleDirection {
|
enum RuleDirection {
|
||||||
@ -459,5 +462,11 @@ message RouteFirewallRule {
|
|||||||
|
|
||||||
// IsDynamic indicates if the route is a DNS route.
|
// IsDynamic indicates if the route is a DNS route.
|
||||||
bool isDynamic = 6;
|
bool isDynamic = 6;
|
||||||
|
|
||||||
|
// Domains is a list of domains for which the rule is applicable.
|
||||||
|
repeated string domains = 7;
|
||||||
|
|
||||||
|
// CustomProtocol is a custom protocol ID.
|
||||||
|
uint32 customProtocol = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AccountRequest holds the result channel to return the requested account.
|
// AccountRequest holds the result channel to return the requested account.
|
||||||
@ -17,19 +20,19 @@ type AccountRequest struct {
|
|||||||
|
|
||||||
// AccountResult holds the account data or an error.
|
// AccountResult holds the account data or an error.
|
||||||
type AccountResult struct {
|
type AccountResult struct {
|
||||||
Account *Account
|
Account *types.Account
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccountRequestBuffer struct {
|
type AccountRequestBuffer struct {
|
||||||
store Store
|
store store.Store
|
||||||
getAccountRequests map[string][]*AccountRequest
|
getAccountRequests map[string][]*AccountRequest
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
getAccountRequestCh chan *AccountRequest
|
getAccountRequestCh chan *AccountRequest
|
||||||
bufferInterval time.Duration
|
bufferInterval time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAccountRequestBuffer(ctx context.Context, store Store) *AccountRequestBuffer {
|
func NewAccountRequestBuffer(ctx context.Context, store store.Store) *AccountRequestBuffer {
|
||||||
bufferIntervalStr := os.Getenv("NB_GET_ACCOUNT_BUFFER_INTERVAL")
|
bufferIntervalStr := os.Getenv("NB_GET_ACCOUNT_BUFFER_INTERVAL")
|
||||||
bufferInterval, err := time.ParseDuration(bufferIntervalStr)
|
bufferInterval, err := time.ParseDuration(bufferIntervalStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -52,7 +55,7 @@ func NewAccountRequestBuffer(ctx context.Context, store Store) *AccountRequestBu
|
|||||||
|
|
||||||
return &ac
|
return &ac
|
||||||
}
|
}
|
||||||
func (ac *AccountRequestBuffer) GetAccountWithBackpressure(ctx context.Context, accountID string) (*Account, error) {
|
func (ac *AccountRequestBuffer) GetAccountWithBackpressure(ctx context.Context, accountID string) (*types.Account, error) {
|
||||||
req := &AccountRequest{
|
req := &AccountRequest{
|
||||||
AccountID: accountID,
|
AccountID: accountID,
|
||||||
ResultChan: make(chan *AccountResult, 1),
|
ResultChan: make(chan *AccountResult, 1),
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -151,6 +151,24 @@ const (
|
|||||||
|
|
||||||
UserGroupPropagationEnabled Activity = 69
|
UserGroupPropagationEnabled Activity = 69
|
||||||
UserGroupPropagationDisabled Activity = 70
|
UserGroupPropagationDisabled Activity = 70
|
||||||
|
|
||||||
|
AccountRoutingPeerDNSResolutionEnabled Activity = 71
|
||||||
|
AccountRoutingPeerDNSResolutionDisabled Activity = 72
|
||||||
|
|
||||||
|
NetworkCreated Activity = 73
|
||||||
|
NetworkUpdated Activity = 74
|
||||||
|
NetworkDeleted Activity = 75
|
||||||
|
|
||||||
|
NetworkResourceCreated Activity = 76
|
||||||
|
NetworkResourceUpdated Activity = 77
|
||||||
|
NetworkResourceDeleted Activity = 78
|
||||||
|
|
||||||
|
NetworkRouterCreated Activity = 79
|
||||||
|
NetworkRouterUpdated Activity = 80
|
||||||
|
NetworkRouterDeleted Activity = 81
|
||||||
|
|
||||||
|
ResourceAddedToGroup Activity = 82
|
||||||
|
ResourceRemovedFromGroup Activity = 83
|
||||||
)
|
)
|
||||||
|
|
||||||
var activityMap = map[Activity]Code{
|
var activityMap = map[Activity]Code{
|
||||||
@ -228,6 +246,24 @@ var activityMap = map[Activity]Code{
|
|||||||
|
|
||||||
UserGroupPropagationEnabled: {"User group propagation enabled", "account.setting.group.propagation.enable"},
|
UserGroupPropagationEnabled: {"User group propagation enabled", "account.setting.group.propagation.enable"},
|
||||||
UserGroupPropagationDisabled: {"User group propagation disabled", "account.setting.group.propagation.disable"},
|
UserGroupPropagationDisabled: {"User group propagation disabled", "account.setting.group.propagation.disable"},
|
||||||
|
|
||||||
|
AccountRoutingPeerDNSResolutionEnabled: {"Account routing peer DNS resolution enabled", "account.setting.routing.peer.dns.resolution.enable"},
|
||||||
|
AccountRoutingPeerDNSResolutionDisabled: {"Account routing peer DNS resolution disabled", "account.setting.routing.peer.dns.resolution.disable"},
|
||||||
|
|
||||||
|
NetworkCreated: {"Network created", "network.create"},
|
||||||
|
NetworkUpdated: {"Network updated", "network.update"},
|
||||||
|
NetworkDeleted: {"Network deleted", "network.delete"},
|
||||||
|
|
||||||
|
NetworkResourceCreated: {"Network resource created", "network.resource.create"},
|
||||||
|
NetworkResourceUpdated: {"Network resource updated", "network.resource.update"},
|
||||||
|
NetworkResourceDeleted: {"Network resource deleted", "network.resource.delete"},
|
||||||
|
|
||||||
|
NetworkRouterCreated: {"Network router created", "network.router.create"},
|
||||||
|
NetworkRouterUpdated: {"Network router updated", "network.router.update"},
|
||||||
|
NetworkRouterDeleted: {"Network router deleted", "network.router.delete"},
|
||||||
|
|
||||||
|
ResourceAddedToGroup: {"Resource added to group", "resource.group.add"},
|
||||||
|
ResourceRemovedFromGroup: {"Resource removed from group", "resource.group.delete"},
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringCode returns a string code of the activity
|
// StringCode returns a string code of the activity
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/idp"
|
"github.com/netbirdio/netbird/management/server/idp"
|
||||||
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -156,7 +157,7 @@ type ProviderConfig struct {
|
|||||||
|
|
||||||
// StoreConfig contains Store configuration
|
// StoreConfig contains Store configuration
|
||||||
type StoreConfig struct {
|
type StoreConfig struct {
|
||||||
Engine StoreEngine
|
Engine store.Engine
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReverseProxy contains reverse proxy configuration in front of management.
|
// ReverseProxy contains reverse proxy configuration in front of management.
|
||||||
|
@ -2,9 +2,7 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@ -12,12 +10,12 @@ import (
|
|||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
"github.com/netbirdio/netbird/management/proto"
|
"github.com/netbirdio/netbird/management/proto"
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
|
"github.com/netbirdio/netbird/management/server/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultTTL = 300
|
|
||||||
|
|
||||||
// DNSConfigCache is a thread-safe cache for DNS configuration components
|
// DNSConfigCache is a thread-safe cache for DNS configuration components
|
||||||
type DNSConfigCache struct {
|
type DNSConfigCache struct {
|
||||||
CustomZones sync.Map
|
CustomZones sync.Map
|
||||||
@ -62,26 +60,9 @@ func (c *DNSConfigCache) SetNameServerGroup(key string, value *proto.NameServerG
|
|||||||
c.NameServerGroups.Store(key, value)
|
c.NameServerGroups.Store(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
type lookupMap map[string]struct{}
|
|
||||||
|
|
||||||
// DNSSettings defines dns settings at the account level
|
|
||||||
type DNSSettings struct {
|
|
||||||
// DisabledManagementGroups groups whose DNS management is disabled
|
|
||||||
DisabledManagementGroups []string `gorm:"serializer:json"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy returns a copy of the DNS settings
|
|
||||||
func (d DNSSettings) Copy() DNSSettings {
|
|
||||||
settings := DNSSettings{
|
|
||||||
DisabledManagementGroups: make([]string, len(d.DisabledManagementGroups)),
|
|
||||||
}
|
|
||||||
copy(settings.DisabledManagementGroups, d.DisabledManagementGroups)
|
|
||||||
return settings
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDNSSettings validates a user role and returns the DNS settings for the provided account ID
|
// GetDNSSettings validates a user role and returns the DNS settings for the provided account ID
|
||||||
func (am *DefaultAccountManager) GetDNSSettings(ctx context.Context, accountID string, userID string) (*DNSSettings, error) {
|
func (am *DefaultAccountManager) GetDNSSettings(ctx context.Context, accountID string, userID string) (*types.DNSSettings, error) {
|
||||||
user, err := am.Store.GetUserByUserID(ctx, LockingStrengthShare, userID)
|
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -94,16 +75,16 @@ func (am *DefaultAccountManager) GetDNSSettings(ctx context.Context, accountID s
|
|||||||
return nil, status.NewAdminPermissionError()
|
return nil, status.NewAdminPermissionError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return am.Store.GetAccountDNSSettings(ctx, LockingStrengthShare, accountID)
|
return am.Store.GetAccountDNSSettings(ctx, store.LockingStrengthShare, accountID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveDNSSettings validates a user role and updates the account's DNS settings
|
// SaveDNSSettings validates a user role and updates the account's DNS settings
|
||||||
func (am *DefaultAccountManager) SaveDNSSettings(ctx context.Context, accountID string, userID string, dnsSettingsToSave *DNSSettings) error {
|
func (am *DefaultAccountManager) SaveDNSSettings(ctx context.Context, accountID string, userID string, dnsSettingsToSave *types.DNSSettings) error {
|
||||||
if dnsSettingsToSave == nil {
|
if dnsSettingsToSave == nil {
|
||||||
return status.Errorf(status.InvalidArgument, "the dns settings provided are nil")
|
return status.Errorf(status.InvalidArgument, "the dns settings provided are nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := am.Store.GetUserByUserID(ctx, LockingStrengthShare, userID)
|
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -119,18 +100,18 @@ func (am *DefaultAccountManager) SaveDNSSettings(ctx context.Context, accountID
|
|||||||
var updateAccountPeers bool
|
var updateAccountPeers bool
|
||||||
var eventsToStore []func()
|
var eventsToStore []func()
|
||||||
|
|
||||||
err = am.Store.ExecuteInTransaction(ctx, func(transaction Store) error {
|
err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
||||||
if err = validateDNSSettings(ctx, transaction, accountID, dnsSettingsToSave); err != nil {
|
if err = validateDNSSettings(ctx, transaction, accountID, dnsSettingsToSave); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
oldSettings, err := transaction.GetAccountDNSSettings(ctx, LockingStrengthUpdate, accountID)
|
oldSettings, err := transaction.GetAccountDNSSettings(ctx, store.LockingStrengthUpdate, accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
addedGroups := difference(dnsSettingsToSave.DisabledManagementGroups, oldSettings.DisabledManagementGroups)
|
addedGroups := util.Difference(dnsSettingsToSave.DisabledManagementGroups, oldSettings.DisabledManagementGroups)
|
||||||
removedGroups := difference(oldSettings.DisabledManagementGroups, dnsSettingsToSave.DisabledManagementGroups)
|
removedGroups := util.Difference(oldSettings.DisabledManagementGroups, dnsSettingsToSave.DisabledManagementGroups)
|
||||||
|
|
||||||
updateAccountPeers, err = areDNSSettingChangesAffectPeers(ctx, transaction, accountID, addedGroups, removedGroups)
|
updateAccountPeers, err = areDNSSettingChangesAffectPeers(ctx, transaction, accountID, addedGroups, removedGroups)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -140,11 +121,11 @@ func (am *DefaultAccountManager) SaveDNSSettings(ctx context.Context, accountID
|
|||||||
events := am.prepareDNSSettingsEvents(ctx, transaction, accountID, userID, addedGroups, removedGroups)
|
events := am.prepareDNSSettingsEvents(ctx, transaction, accountID, userID, addedGroups, removedGroups)
|
||||||
eventsToStore = append(eventsToStore, events...)
|
eventsToStore = append(eventsToStore, events...)
|
||||||
|
|
||||||
if err = transaction.IncrementNetworkSerial(ctx, LockingStrengthUpdate, accountID); err != nil {
|
if err = transaction.IncrementNetworkSerial(ctx, store.LockingStrengthUpdate, accountID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return transaction.SaveDNSSettings(ctx, LockingStrengthUpdate, accountID, dnsSettingsToSave)
|
return transaction.SaveDNSSettings(ctx, store.LockingStrengthUpdate, accountID, dnsSettingsToSave)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -155,18 +136,18 @@ func (am *DefaultAccountManager) SaveDNSSettings(ctx context.Context, accountID
|
|||||||
}
|
}
|
||||||
|
|
||||||
if updateAccountPeers {
|
if updateAccountPeers {
|
||||||
am.updateAccountPeers(ctx, accountID)
|
am.UpdateAccountPeers(ctx, accountID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareDNSSettingsEvents prepares a list of event functions to be stored.
|
// prepareDNSSettingsEvents prepares a list of event functions to be stored.
|
||||||
func (am *DefaultAccountManager) prepareDNSSettingsEvents(ctx context.Context, transaction Store, accountID, userID string, addedGroups, removedGroups []string) []func() {
|
func (am *DefaultAccountManager) prepareDNSSettingsEvents(ctx context.Context, transaction store.Store, accountID, userID string, addedGroups, removedGroups []string) []func() {
|
||||||
var eventsToStore []func()
|
var eventsToStore []func()
|
||||||
|
|
||||||
modifiedGroups := slices.Concat(addedGroups, removedGroups)
|
modifiedGroups := slices.Concat(addedGroups, removedGroups)
|
||||||
groups, err := transaction.GetGroupsByIDs(ctx, LockingStrengthShare, accountID, modifiedGroups)
|
groups, err := transaction.GetGroupsByIDs(ctx, store.LockingStrengthShare, accountID, modifiedGroups)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithContext(ctx).Debugf("failed to get groups for dns settings events: %v", err)
|
log.WithContext(ctx).Debugf("failed to get groups for dns settings events: %v", err)
|
||||||
return nil
|
return nil
|
||||||
@ -203,8 +184,8 @@ func (am *DefaultAccountManager) prepareDNSSettingsEvents(ctx context.Context, t
|
|||||||
}
|
}
|
||||||
|
|
||||||
// areDNSSettingChangesAffectPeers checks if the DNS settings changes affect any peers.
|
// areDNSSettingChangesAffectPeers checks if the DNS settings changes affect any peers.
|
||||||
func areDNSSettingChangesAffectPeers(ctx context.Context, transaction Store, accountID string, addedGroups, removedGroups []string) (bool, error) {
|
func areDNSSettingChangesAffectPeers(ctx context.Context, transaction store.Store, accountID string, addedGroups, removedGroups []string) (bool, error) {
|
||||||
hasPeers, err := anyGroupHasPeers(ctx, transaction, accountID, addedGroups)
|
hasPeers, err := anyGroupHasPeersOrResources(ctx, transaction, accountID, addedGroups)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@ -213,16 +194,16 @@ func areDNSSettingChangesAffectPeers(ctx context.Context, transaction Store, acc
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return anyGroupHasPeers(ctx, transaction, accountID, removedGroups)
|
return anyGroupHasPeersOrResources(ctx, transaction, accountID, removedGroups)
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateDNSSettings validates the DNS settings.
|
// validateDNSSettings validates the DNS settings.
|
||||||
func validateDNSSettings(ctx context.Context, transaction Store, accountID string, settings *DNSSettings) error {
|
func validateDNSSettings(ctx context.Context, transaction store.Store, accountID string, settings *types.DNSSettings) error {
|
||||||
if len(settings.DisabledManagementGroups) == 0 {
|
if len(settings.DisabledManagementGroups) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
groups, err := transaction.GetGroupsByIDs(ctx, LockingStrengthShare, accountID, settings.DisabledManagementGroups)
|
groups, err := transaction.GetGroupsByIDs(ctx, store.LockingStrengthShare, accountID, settings.DisabledManagementGroups)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -298,81 +279,3 @@ func convertToProtoNameServerGroup(nsGroup *nbdns.NameServerGroup) *proto.NameSe
|
|||||||
}
|
}
|
||||||
return protoGroup
|
return protoGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPeerNSGroups(account *Account, peerID string) []*nbdns.NameServerGroup {
|
|
||||||
groupList := account.getPeerGroups(peerID)
|
|
||||||
|
|
||||||
var peerNSGroups []*nbdns.NameServerGroup
|
|
||||||
|
|
||||||
for _, nsGroup := range account.NameServerGroups {
|
|
||||||
if !nsGroup.Enabled {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, gID := range nsGroup.Groups {
|
|
||||||
_, found := groupList[gID]
|
|
||||||
if found {
|
|
||||||
if !peerIsNameserver(account.GetPeer(peerID), nsGroup) {
|
|
||||||
peerNSGroups = append(peerNSGroups, nsGroup.Copy())
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return peerNSGroups
|
|
||||||
}
|
|
||||||
|
|
||||||
// peerIsNameserver returns true if the peer is a nameserver for a nsGroup
|
|
||||||
func peerIsNameserver(peer *nbpeer.Peer, nsGroup *nbdns.NameServerGroup) bool {
|
|
||||||
for _, ns := range nsGroup.NameServers {
|
|
||||||
if peer.IP.Equal(ns.IP.AsSlice()) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func addPeerLabelsToAccount(ctx context.Context, account *Account, peerLabels lookupMap) {
|
|
||||||
for _, peer := range account.Peers {
|
|
||||||
label, err := getPeerHostLabel(peer.Name, peerLabels)
|
|
||||||
if err != nil {
|
|
||||||
log.WithContext(ctx).Errorf("got an error while generating a peer host label. Peer name %s, error: %v. Trying with the peer's meta hostname", peer.Name, err)
|
|
||||||
label, err = getPeerHostLabel(peer.Meta.Hostname, peerLabels)
|
|
||||||
if err != nil {
|
|
||||||
log.WithContext(ctx).Errorf("got another error while generating a peer host label with hostname. Peer hostname %s, error: %v. Skipping", peer.Meta.Hostname, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
peer.DNSLabel = label
|
|
||||||
peerLabels[label] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPeerHostLabel(name string, peerLabels lookupMap) (string, error) {
|
|
||||||
label, err := nbdns.GetParsedDomainLabel(name)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
uniqueLabel := getUniqueHostLabel(label, peerLabels)
|
|
||||||
if uniqueLabel == "" {
|
|
||||||
return "", fmt.Errorf("couldn't find a unique valid label for %s, parsed label %s", name, label)
|
|
||||||
}
|
|
||||||
return uniqueLabel, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getUniqueHostLabel look for a unique host label, and if doesn't find add a suffix up to 999
|
|
||||||
func getUniqueHostLabel(name string, peerLabels lookupMap) string {
|
|
||||||
_, found := peerLabels[name]
|
|
||||||
if !found {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
for i := 1; i < 1000; i++ {
|
|
||||||
nameWithSuffix := name + "-" + strconv.Itoa(i)
|
|
||||||
_, found = peerLabels[nameWithSuffix]
|
|
||||||
if !found {
|
|
||||||
return nameWithSuffix
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
@ -11,13 +11,14 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/dns"
|
"github.com/netbirdio/netbird/dns"
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
"github.com/netbirdio/netbird/management/server/group"
|
|
||||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
)
|
)
|
||||||
@ -53,7 +54,7 @@ func TestGetDNSSettings(t *testing.T) {
|
|||||||
t.Fatal("DNS settings for new accounts shouldn't return nil")
|
t.Fatal("DNS settings for new accounts shouldn't return nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
account.DNSSettings = DNSSettings{
|
account.DNSSettings = types.DNSSettings{
|
||||||
DisabledManagementGroups: []string{group1ID},
|
DisabledManagementGroups: []string{group1ID},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,20 +87,20 @@ func TestSaveDNSSettings(t *testing.T) {
|
|||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
userID string
|
userID string
|
||||||
inputSettings *DNSSettings
|
inputSettings *types.DNSSettings
|
||||||
shouldFail bool
|
shouldFail bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Saving As Admin Should Be OK",
|
name: "Saving As Admin Should Be OK",
|
||||||
userID: dnsAdminUserID,
|
userID: dnsAdminUserID,
|
||||||
inputSettings: &DNSSettings{
|
inputSettings: &types.DNSSettings{
|
||||||
DisabledManagementGroups: []string{dnsGroup1ID},
|
DisabledManagementGroups: []string{dnsGroup1ID},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Should Not Update Settings As Regular User",
|
name: "Should Not Update Settings As Regular User",
|
||||||
userID: dnsRegularUserID,
|
userID: dnsRegularUserID,
|
||||||
inputSettings: &DNSSettings{
|
inputSettings: &types.DNSSettings{
|
||||||
DisabledManagementGroups: []string{dnsGroup1ID},
|
DisabledManagementGroups: []string{dnsGroup1ID},
|
||||||
},
|
},
|
||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
@ -113,7 +114,7 @@ func TestSaveDNSSettings(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Should Not Update Settings If Group Is Invalid",
|
name: "Should Not Update Settings If Group Is Invalid",
|
||||||
userID: dnsAdminUserID,
|
userID: dnsAdminUserID,
|
||||||
inputSettings: &DNSSettings{
|
inputSettings: &types.DNSSettings{
|
||||||
DisabledManagementGroups: []string{"non-existing-group"},
|
DisabledManagementGroups: []string{"non-existing-group"},
|
||||||
},
|
},
|
||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
@ -210,10 +211,10 @@ func createDNSManager(t *testing.T) (*DefaultAccountManager, error) {
|
|||||||
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedValidator{}, metrics)
|
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedValidator{}, metrics)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDNSStore(t *testing.T) (Store, error) {
|
func createDNSStore(t *testing.T) (store.Store, error) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
dataDir := t.TempDir()
|
dataDir := t.TempDir()
|
||||||
store, cleanUp, err := NewTestStoreFromSQL(context.Background(), "", dataDir)
|
store, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "", dataDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -222,7 +223,7 @@ func createDNSStore(t *testing.T) (Store, error) {
|
|||||||
return store, nil
|
return store, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initTestDNSAccount(t *testing.T, am *DefaultAccountManager) (*Account, error) {
|
func initTestDNSAccount(t *testing.T, am *DefaultAccountManager) (*types.Account, error) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
peer1 := &nbpeer.Peer{
|
peer1 := &nbpeer.Peer{
|
||||||
Key: dnsPeer1Key,
|
Key: dnsPeer1Key,
|
||||||
@ -259,9 +260,9 @@ func initTestDNSAccount(t *testing.T, am *DefaultAccountManager) (*Account, erro
|
|||||||
|
|
||||||
account := newAccountWithId(context.Background(), dnsAccountID, dnsAdminUserID, domain)
|
account := newAccountWithId(context.Background(), dnsAccountID, dnsAdminUserID, domain)
|
||||||
|
|
||||||
account.Users[dnsRegularUserID] = &User{
|
account.Users[dnsRegularUserID] = &types.User{
|
||||||
Id: dnsRegularUserID,
|
Id: dnsRegularUserID,
|
||||||
Role: UserRoleUser,
|
Role: types.UserRoleUser,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := am.Store.SaveAccount(context.Background(), account)
|
err := am.Store.SaveAccount(context.Background(), account)
|
||||||
@ -293,13 +294,13 @@ func initTestDNSAccount(t *testing.T, am *DefaultAccountManager) (*Account, erro
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newGroup1 := &group.Group{
|
newGroup1 := &types.Group{
|
||||||
ID: dnsGroup1ID,
|
ID: dnsGroup1ID,
|
||||||
Peers: []string{peer1.ID},
|
Peers: []string{peer1.ID},
|
||||||
Name: dnsGroup1ID,
|
Name: dnsGroup1ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
newGroup2 := &group.Group{
|
newGroup2 := &types.Group{
|
||||||
ID: dnsGroup2ID,
|
ID: dnsGroup2ID,
|
||||||
Name: dnsGroup2ID,
|
Name: dnsGroup2ID,
|
||||||
}
|
}
|
||||||
@ -483,7 +484,7 @@ func TestToProtocolDNSConfigWithCache(t *testing.T) {
|
|||||||
func TestDNSAccountPeersUpdate(t *testing.T) {
|
func TestDNSAccountPeersUpdate(t *testing.T) {
|
||||||
manager, account, peer1, peer2, peer3 := setupNetworkMapTest(t)
|
manager, account, peer1, peer2, peer3 := setupNetworkMapTest(t)
|
||||||
|
|
||||||
err := manager.SaveGroups(context.Background(), account.Id, userID, []*group.Group{
|
err := manager.SaveGroups(context.Background(), account.Id, userID, []*types.Group{
|
||||||
{
|
{
|
||||||
ID: "groupA",
|
ID: "groupA",
|
||||||
Name: "GroupA",
|
Name: "GroupA",
|
||||||
@ -510,7 +511,7 @@ func TestDNSAccountPeersUpdate(t *testing.T) {
|
|||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := manager.SaveDNSSettings(context.Background(), account.Id, userID, &DNSSettings{
|
err := manager.SaveDNSSettings(context.Background(), account.Id, userID, &types.DNSSettings{
|
||||||
DisabledManagementGroups: []string{"groupA"},
|
DisabledManagementGroups: []string{"groupA"},
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -550,7 +551,7 @@ func TestDNSAccountPeersUpdate(t *testing.T) {
|
|||||||
|
|
||||||
// Creating DNS settings with groups that have peers should update account peers and send peer update
|
// Creating DNS settings with groups that have peers should update account peers and send peer update
|
||||||
t.Run("creating dns setting with used groups", func(t *testing.T) {
|
t.Run("creating dns setting with used groups", func(t *testing.T) {
|
||||||
err = manager.SaveGroup(context.Background(), account.Id, userID, &group.Group{
|
err = manager.SaveGroup(context.Background(), account.Id, userID, &types.Group{
|
||||||
ID: "groupA",
|
ID: "groupA",
|
||||||
Name: "GroupA",
|
Name: "GroupA",
|
||||||
Peers: []string{peer1.ID, peer2.ID, peer3.ID},
|
Peers: []string{peer1.ID, peer2.ID, peer3.ID},
|
||||||
@ -589,7 +590,7 @@ func TestDNSAccountPeersUpdate(t *testing.T) {
|
|||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := manager.SaveDNSSettings(context.Background(), account.Id, userID, &DNSSettings{
|
err := manager.SaveDNSSettings(context.Background(), account.Id, userID, &types.DNSSettings{
|
||||||
DisabledManagementGroups: []string{"groupA", "groupB"},
|
DisabledManagementGroups: []string{"groupA", "groupB"},
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -609,7 +610,7 @@ func TestDNSAccountPeersUpdate(t *testing.T) {
|
|||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := manager.SaveDNSSettings(context.Background(), account.Id, userID, &DNSSettings{
|
err := manager.SaveDNSSettings(context.Background(), account.Id, userID, &types.DNSSettings{
|
||||||
DisabledManagementGroups: []string{"groupA"},
|
DisabledManagementGroups: []string{"groupA"},
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -629,7 +630,7 @@ func TestDNSAccountPeersUpdate(t *testing.T) {
|
|||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := manager.SaveDNSSettings(context.Background(), account.Id, userID, &DNSSettings{
|
err := manager.SaveDNSSettings(context.Background(), account.Id, userID, &types.DNSSettings{
|
||||||
DisabledManagementGroups: []string{},
|
DisabledManagementGroups: []string{},
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -9,6 +9,8 @@ import (
|
|||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||||
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -21,7 +23,7 @@ var (
|
|||||||
|
|
||||||
type ephemeralPeer struct {
|
type ephemeralPeer struct {
|
||||||
id string
|
id string
|
||||||
account *Account
|
account *types.Account
|
||||||
deadline time.Time
|
deadline time.Time
|
||||||
next *ephemeralPeer
|
next *ephemeralPeer
|
||||||
}
|
}
|
||||||
@ -32,7 +34,7 @@ type ephemeralPeer struct {
|
|||||||
// EphemeralManager keep a list of ephemeral peers. After ephemeralLifeTime inactivity the peer will be deleted
|
// EphemeralManager keep a list of ephemeral peers. After ephemeralLifeTime inactivity the peer will be deleted
|
||||||
// automatically. Inactivity means the peer disconnected from the Management server.
|
// automatically. Inactivity means the peer disconnected from the Management server.
|
||||||
type EphemeralManager struct {
|
type EphemeralManager struct {
|
||||||
store Store
|
store store.Store
|
||||||
accountManager AccountManager
|
accountManager AccountManager
|
||||||
|
|
||||||
headPeer *ephemeralPeer
|
headPeer *ephemeralPeer
|
||||||
@ -42,7 +44,7 @@ type EphemeralManager struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewEphemeralManager instantiate new EphemeralManager
|
// NewEphemeralManager instantiate new EphemeralManager
|
||||||
func NewEphemeralManager(store Store, accountManager AccountManager) *EphemeralManager {
|
func NewEphemeralManager(store store.Store, accountManager AccountManager) *EphemeralManager {
|
||||||
return &EphemeralManager{
|
return &EphemeralManager{
|
||||||
store: store,
|
store: store,
|
||||||
accountManager: accountManager,
|
accountManager: accountManager,
|
||||||
@ -177,7 +179,7 @@ func (e *EphemeralManager) cleanup(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EphemeralManager) addPeer(id string, account *Account, deadline time.Time) {
|
func (e *EphemeralManager) addPeer(id string, account *types.Account, deadline time.Time) {
|
||||||
ep := &ephemeralPeer{
|
ep := &ephemeralPeer{
|
||||||
id: id,
|
id: id,
|
||||||
account: account,
|
account: account,
|
||||||
|
@ -8,18 +8,20 @@ import (
|
|||||||
|
|
||||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MockStore struct {
|
type MockStore struct {
|
||||||
Store
|
store.Store
|
||||||
account *Account
|
account *types.Account
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MockStore) GetAllAccounts(_ context.Context) []*Account {
|
func (s *MockStore) GetAllAccounts(_ context.Context) []*types.Account {
|
||||||
return []*Account{s.account}
|
return []*types.Account{s.account}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MockStore) GetAccountByPeerID(_ context.Context, peerId string) (*Account, error) {
|
func (s *MockStore) GetAccountByPeerID(_ context.Context, peerId string) (*types.Account, error) {
|
||||||
_, ok := s.account.Peers[peerId]
|
_, ok := s.account.Peers[peerId]
|
||||||
if ok {
|
if ok {
|
||||||
return s.account, nil
|
return s.account, nil
|
||||||
|
@ -10,10 +10,12 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
|
"github.com/netbirdio/netbird/management/server/util"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
nbgroup "github.com/netbirdio/netbird/management/server/group"
|
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,7 +30,7 @@ func (e *GroupLinkError) Error() string {
|
|||||||
|
|
||||||
// CheckGroupPermissions validates if a user has the necessary permissions to view groups
|
// CheckGroupPermissions validates if a user has the necessary permissions to view groups
|
||||||
func (am *DefaultAccountManager) CheckGroupPermissions(ctx context.Context, accountID, userID string) error {
|
func (am *DefaultAccountManager) CheckGroupPermissions(ctx context.Context, accountID, userID string) error {
|
||||||
user, err := am.Store.GetUserByUserID(ctx, LockingStrengthShare, userID)
|
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -45,38 +47,38 @@ func (am *DefaultAccountManager) CheckGroupPermissions(ctx context.Context, acco
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetGroup returns a specific group by groupID in an account
|
// GetGroup returns a specific group by groupID in an account
|
||||||
func (am *DefaultAccountManager) GetGroup(ctx context.Context, accountID, groupID, userID string) (*nbgroup.Group, error) {
|
func (am *DefaultAccountManager) GetGroup(ctx context.Context, accountID, groupID, userID string) (*types.Group, error) {
|
||||||
if err := am.CheckGroupPermissions(ctx, accountID, userID); err != nil {
|
if err := am.CheckGroupPermissions(ctx, accountID, userID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return am.Store.GetGroupByID(ctx, LockingStrengthShare, accountID, groupID)
|
return am.Store.GetGroupByID(ctx, store.LockingStrengthShare, accountID, groupID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllGroups returns all groups in an account
|
// GetAllGroups returns all groups in an account
|
||||||
func (am *DefaultAccountManager) GetAllGroups(ctx context.Context, accountID, userID string) ([]*nbgroup.Group, error) {
|
func (am *DefaultAccountManager) GetAllGroups(ctx context.Context, accountID, userID string) ([]*types.Group, error) {
|
||||||
if err := am.CheckGroupPermissions(ctx, accountID, userID); err != nil {
|
if err := am.CheckGroupPermissions(ctx, accountID, userID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return am.Store.GetAccountGroups(ctx, LockingStrengthShare, accountID)
|
return am.Store.GetAccountGroups(ctx, store.LockingStrengthShare, accountID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGroupByName filters all groups in an account by name and returns the one with the most peers
|
// GetGroupByName filters all groups in an account by name and returns the one with the most peers
|
||||||
func (am *DefaultAccountManager) GetGroupByName(ctx context.Context, groupName, accountID string) (*nbgroup.Group, error) {
|
func (am *DefaultAccountManager) GetGroupByName(ctx context.Context, groupName, accountID string) (*types.Group, error) {
|
||||||
return am.Store.GetGroupByName(ctx, LockingStrengthShare, accountID, groupName)
|
return am.Store.GetGroupByName(ctx, store.LockingStrengthShare, accountID, groupName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveGroup object of the peers
|
// SaveGroup object of the peers
|
||||||
func (am *DefaultAccountManager) SaveGroup(ctx context.Context, accountID, userID string, newGroup *nbgroup.Group) error {
|
func (am *DefaultAccountManager) SaveGroup(ctx context.Context, accountID, userID string, newGroup *types.Group) error {
|
||||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
return am.SaveGroups(ctx, accountID, userID, []*nbgroup.Group{newGroup})
|
return am.SaveGroups(ctx, accountID, userID, []*types.Group{newGroup})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveGroups adds new groups to the account.
|
// SaveGroups adds new groups to the account.
|
||||||
// Note: This function does not acquire the global lock.
|
// Note: This function does not acquire the global lock.
|
||||||
// It is the caller's responsibility to ensure proper locking is in place before invoking this method.
|
// It is the caller's responsibility to ensure proper locking is in place before invoking this method.
|
||||||
func (am *DefaultAccountManager) SaveGroups(ctx context.Context, accountID, userID string, groups []*nbgroup.Group) error {
|
func (am *DefaultAccountManager) SaveGroups(ctx context.Context, accountID, userID string, groups []*types.Group) error {
|
||||||
user, err := am.Store.GetUserByUserID(ctx, LockingStrengthShare, userID)
|
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -90,10 +92,10 @@ func (am *DefaultAccountManager) SaveGroups(ctx context.Context, accountID, user
|
|||||||
}
|
}
|
||||||
|
|
||||||
var eventsToStore []func()
|
var eventsToStore []func()
|
||||||
var groupsToSave []*nbgroup.Group
|
var groupsToSave []*types.Group
|
||||||
var updateAccountPeers bool
|
var updateAccountPeers bool
|
||||||
|
|
||||||
err = am.Store.ExecuteInTransaction(ctx, func(transaction Store) error {
|
err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
||||||
groupIDs := make([]string, 0, len(groups))
|
groupIDs := make([]string, 0, len(groups))
|
||||||
for _, newGroup := range groups {
|
for _, newGroup := range groups {
|
||||||
if err = validateNewGroup(ctx, transaction, accountID, newGroup); err != nil {
|
if err = validateNewGroup(ctx, transaction, accountID, newGroup); err != nil {
|
||||||
@ -113,11 +115,11 @@ func (am *DefaultAccountManager) SaveGroups(ctx context.Context, accountID, user
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = transaction.IncrementNetworkSerial(ctx, LockingStrengthUpdate, accountID); err != nil {
|
if err = transaction.IncrementNetworkSerial(ctx, store.LockingStrengthUpdate, accountID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return transaction.SaveGroups(ctx, LockingStrengthUpdate, groupsToSave)
|
return transaction.SaveGroups(ctx, store.LockingStrengthUpdate, groupsToSave)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -128,23 +130,23 @@ func (am *DefaultAccountManager) SaveGroups(ctx context.Context, accountID, user
|
|||||||
}
|
}
|
||||||
|
|
||||||
if updateAccountPeers {
|
if updateAccountPeers {
|
||||||
am.updateAccountPeers(ctx, accountID)
|
am.UpdateAccountPeers(ctx, accountID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareGroupEvents prepares a list of event functions to be stored.
|
// prepareGroupEvents prepares a list of event functions to be stored.
|
||||||
func (am *DefaultAccountManager) prepareGroupEvents(ctx context.Context, transaction Store, accountID, userID string, newGroup *nbgroup.Group) []func() {
|
func (am *DefaultAccountManager) prepareGroupEvents(ctx context.Context, transaction store.Store, accountID, userID string, newGroup *types.Group) []func() {
|
||||||
var eventsToStore []func()
|
var eventsToStore []func()
|
||||||
|
|
||||||
addedPeers := make([]string, 0)
|
addedPeers := make([]string, 0)
|
||||||
removedPeers := make([]string, 0)
|
removedPeers := make([]string, 0)
|
||||||
|
|
||||||
oldGroup, err := transaction.GetGroupByID(ctx, LockingStrengthShare, accountID, newGroup.ID)
|
oldGroup, err := transaction.GetGroupByID(ctx, store.LockingStrengthShare, accountID, newGroup.ID)
|
||||||
if err == nil && oldGroup != nil {
|
if err == nil && oldGroup != nil {
|
||||||
addedPeers = difference(newGroup.Peers, oldGroup.Peers)
|
addedPeers = util.Difference(newGroup.Peers, oldGroup.Peers)
|
||||||
removedPeers = difference(oldGroup.Peers, newGroup.Peers)
|
removedPeers = util.Difference(oldGroup.Peers, newGroup.Peers)
|
||||||
} else {
|
} else {
|
||||||
addedPeers = append(addedPeers, newGroup.Peers...)
|
addedPeers = append(addedPeers, newGroup.Peers...)
|
||||||
eventsToStore = append(eventsToStore, func() {
|
eventsToStore = append(eventsToStore, func() {
|
||||||
@ -153,7 +155,7 @@ func (am *DefaultAccountManager) prepareGroupEvents(ctx context.Context, transac
|
|||||||
}
|
}
|
||||||
|
|
||||||
modifiedPeers := slices.Concat(addedPeers, removedPeers)
|
modifiedPeers := slices.Concat(addedPeers, removedPeers)
|
||||||
peers, err := transaction.GetPeersByIDs(ctx, LockingStrengthShare, accountID, modifiedPeers)
|
peers, err := transaction.GetPeersByIDs(ctx, store.LockingStrengthShare, accountID, modifiedPeers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithContext(ctx).Debugf("failed to get peers for group events: %v", err)
|
log.WithContext(ctx).Debugf("failed to get peers for group events: %v", err)
|
||||||
return nil
|
return nil
|
||||||
@ -194,21 +196,6 @@ func (am *DefaultAccountManager) prepareGroupEvents(ctx context.Context, transac
|
|||||||
return eventsToStore
|
return eventsToStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// difference returns the elements in `a` that aren't in `b`.
|
|
||||||
func difference(a, b []string) []string {
|
|
||||||
mb := make(map[string]struct{}, len(b))
|
|
||||||
for _, x := range b {
|
|
||||||
mb[x] = struct{}{}
|
|
||||||
}
|
|
||||||
var diff []string
|
|
||||||
for _, x := range a {
|
|
||||||
if _, found := mb[x]; !found {
|
|
||||||
diff = append(diff, x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return diff
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteGroup object of the peers.
|
// DeleteGroup object of the peers.
|
||||||
func (am *DefaultAccountManager) DeleteGroup(ctx context.Context, accountID, userID, groupID string) error {
|
func (am *DefaultAccountManager) DeleteGroup(ctx context.Context, accountID, userID, groupID string) error {
|
||||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||||
@ -223,7 +210,7 @@ func (am *DefaultAccountManager) DeleteGroup(ctx context.Context, accountID, use
|
|||||||
// If an error occurs while deleting a group, the function skips it and continues deleting other groups.
|
// If an error occurs while deleting a group, the function skips it and continues deleting other groups.
|
||||||
// Errors are collected and returned at the end.
|
// Errors are collected and returned at the end.
|
||||||
func (am *DefaultAccountManager) DeleteGroups(ctx context.Context, accountID, userID string, groupIDs []string) error {
|
func (am *DefaultAccountManager) DeleteGroups(ctx context.Context, accountID, userID string, groupIDs []string) error {
|
||||||
user, err := am.Store.GetUserByUserID(ctx, LockingStrengthShare, userID)
|
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -238,11 +225,11 @@ func (am *DefaultAccountManager) DeleteGroups(ctx context.Context, accountID, us
|
|||||||
|
|
||||||
var allErrors error
|
var allErrors error
|
||||||
var groupIDsToDelete []string
|
var groupIDsToDelete []string
|
||||||
var deletedGroups []*nbgroup.Group
|
var deletedGroups []*types.Group
|
||||||
|
|
||||||
err = am.Store.ExecuteInTransaction(ctx, func(transaction Store) error {
|
err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
||||||
for _, groupID := range groupIDs {
|
for _, groupID := range groupIDs {
|
||||||
group, err := transaction.GetGroupByID(ctx, LockingStrengthUpdate, accountID, groupID)
|
group, err := transaction.GetGroupByID(ctx, store.LockingStrengthUpdate, accountID, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
allErrors = errors.Join(allErrors, err)
|
allErrors = errors.Join(allErrors, err)
|
||||||
continue
|
continue
|
||||||
@ -257,11 +244,11 @@ func (am *DefaultAccountManager) DeleteGroups(ctx context.Context, accountID, us
|
|||||||
deletedGroups = append(deletedGroups, group)
|
deletedGroups = append(deletedGroups, group)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = transaction.IncrementNetworkSerial(ctx, LockingStrengthUpdate, accountID); err != nil {
|
if err = transaction.IncrementNetworkSerial(ctx, store.LockingStrengthUpdate, accountID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return transaction.DeleteGroups(ctx, LockingStrengthUpdate, accountID, groupIDsToDelete)
|
return transaction.DeleteGroups(ctx, store.LockingStrengthUpdate, accountID, groupIDsToDelete)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -279,12 +266,12 @@ func (am *DefaultAccountManager) GroupAddPeer(ctx context.Context, accountID, gr
|
|||||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
var group *nbgroup.Group
|
var group *types.Group
|
||||||
var updateAccountPeers bool
|
var updateAccountPeers bool
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
err = am.Store.ExecuteInTransaction(ctx, func(transaction Store) error {
|
err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
||||||
group, err = transaction.GetGroupByID(context.Background(), LockingStrengthUpdate, accountID, groupID)
|
group, err = transaction.GetGroupByID(context.Background(), store.LockingStrengthUpdate, accountID, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -298,18 +285,59 @@ func (am *DefaultAccountManager) GroupAddPeer(ctx context.Context, accountID, gr
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = transaction.IncrementNetworkSerial(ctx, LockingStrengthUpdate, accountID); err != nil {
|
if err = transaction.IncrementNetworkSerial(ctx, store.LockingStrengthUpdate, accountID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return transaction.SaveGroup(ctx, LockingStrengthUpdate, group)
|
return transaction.SaveGroup(ctx, store.LockingStrengthUpdate, group)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if updateAccountPeers {
|
if updateAccountPeers {
|
||||||
am.updateAccountPeers(ctx, accountID)
|
am.UpdateAccountPeers(ctx, accountID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupAddResource appends resource to the group
|
||||||
|
func (am *DefaultAccountManager) GroupAddResource(ctx context.Context, accountID, groupID string, resource types.Resource) error {
|
||||||
|
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||||
|
defer unlock()
|
||||||
|
|
||||||
|
var group *types.Group
|
||||||
|
var updateAccountPeers bool
|
||||||
|
var err error
|
||||||
|
|
||||||
|
err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
||||||
|
group, err = transaction.GetGroupByID(context.Background(), store.LockingStrengthUpdate, accountID, groupID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if updated := group.AddResource(resource); !updated {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAccountPeers, err = areGroupChangesAffectPeers(ctx, transaction, accountID, []string{groupID})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = transaction.IncrementNetworkSerial(ctx, store.LockingStrengthUpdate, accountID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return transaction.SaveGroup(ctx, store.LockingStrengthUpdate, group)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if updateAccountPeers {
|
||||||
|
am.UpdateAccountPeers(ctx, accountID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -320,12 +348,12 @@ func (am *DefaultAccountManager) GroupDeletePeer(ctx context.Context, accountID,
|
|||||||
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
var group *nbgroup.Group
|
var group *types.Group
|
||||||
var updateAccountPeers bool
|
var updateAccountPeers bool
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
err = am.Store.ExecuteInTransaction(ctx, func(transaction Store) error {
|
err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
||||||
group, err = transaction.GetGroupByID(context.Background(), LockingStrengthUpdate, accountID, groupID)
|
group, err = transaction.GetGroupByID(context.Background(), store.LockingStrengthUpdate, accountID, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -339,31 +367,72 @@ func (am *DefaultAccountManager) GroupDeletePeer(ctx context.Context, accountID,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = transaction.IncrementNetworkSerial(ctx, LockingStrengthUpdate, accountID); err != nil {
|
if err = transaction.IncrementNetworkSerial(ctx, store.LockingStrengthUpdate, accountID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return transaction.SaveGroup(ctx, LockingStrengthUpdate, group)
|
return transaction.SaveGroup(ctx, store.LockingStrengthUpdate, group)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if updateAccountPeers {
|
if updateAccountPeers {
|
||||||
am.updateAccountPeers(ctx, accountID)
|
am.UpdateAccountPeers(ctx, accountID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupDeleteResource removes resource from the group
|
||||||
|
func (am *DefaultAccountManager) GroupDeleteResource(ctx context.Context, accountID, groupID string, resource types.Resource) error {
|
||||||
|
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
|
||||||
|
defer unlock()
|
||||||
|
|
||||||
|
var group *types.Group
|
||||||
|
var updateAccountPeers bool
|
||||||
|
var err error
|
||||||
|
|
||||||
|
err = am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
|
||||||
|
group, err = transaction.GetGroupByID(context.Background(), store.LockingStrengthUpdate, accountID, groupID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if updated := group.RemoveResource(resource); !updated {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAccountPeers, err = areGroupChangesAffectPeers(ctx, transaction, accountID, []string{groupID})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = transaction.IncrementNetworkSerial(ctx, store.LockingStrengthUpdate, accountID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return transaction.SaveGroup(ctx, store.LockingStrengthUpdate, group)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if updateAccountPeers {
|
||||||
|
am.UpdateAccountPeers(ctx, accountID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateNewGroup validates the new group for existence and required fields.
|
// validateNewGroup validates the new group for existence and required fields.
|
||||||
func validateNewGroup(ctx context.Context, transaction Store, accountID string, newGroup *nbgroup.Group) error {
|
func validateNewGroup(ctx context.Context, transaction store.Store, accountID string, newGroup *types.Group) error {
|
||||||
if newGroup.ID == "" && newGroup.Issued != nbgroup.GroupIssuedAPI {
|
if newGroup.ID == "" && newGroup.Issued != types.GroupIssuedAPI {
|
||||||
return status.Errorf(status.InvalidArgument, "%s group without ID set", newGroup.Issued)
|
return status.Errorf(status.InvalidArgument, "%s group without ID set", newGroup.Issued)
|
||||||
}
|
}
|
||||||
|
|
||||||
if newGroup.ID == "" && newGroup.Issued == nbgroup.GroupIssuedAPI {
|
if newGroup.ID == "" && newGroup.Issued == types.GroupIssuedAPI {
|
||||||
existingGroup, err := transaction.GetGroupByName(ctx, LockingStrengthShare, accountID, newGroup.Name)
|
existingGroup, err := transaction.GetGroupByName(ctx, store.LockingStrengthShare, accountID, newGroup.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if s, ok := status.FromError(err); !ok || s.Type() != status.NotFound {
|
if s, ok := status.FromError(err); !ok || s.Type() != status.NotFound {
|
||||||
return err
|
return err
|
||||||
@ -380,7 +449,7 @@ func validateNewGroup(ctx context.Context, transaction Store, accountID string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, peerID := range newGroup.Peers {
|
for _, peerID := range newGroup.Peers {
|
||||||
_, err := transaction.GetPeerByID(ctx, LockingStrengthShare, accountID, peerID)
|
_, err := transaction.GetPeerByID(ctx, store.LockingStrengthShare, accountID, peerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return status.Errorf(status.InvalidArgument, "peer with ID \"%s\" not found", peerID)
|
return status.Errorf(status.InvalidArgument, "peer with ID \"%s\" not found", peerID)
|
||||||
}
|
}
|
||||||
@ -389,14 +458,14 @@ func validateNewGroup(ctx context.Context, transaction Store, accountID string,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateDeleteGroup(ctx context.Context, transaction Store, group *nbgroup.Group, userID string) error {
|
func validateDeleteGroup(ctx context.Context, transaction store.Store, group *types.Group, userID string) error {
|
||||||
// disable a deleting integration group if the initiator is not an admin service user
|
// disable a deleting integration group if the initiator is not an admin service user
|
||||||
if group.Issued == nbgroup.GroupIssuedIntegration {
|
if group.Issued == types.GroupIssuedIntegration {
|
||||||
executingUser, err := transaction.GetUserByUserID(ctx, LockingStrengthShare, userID)
|
executingUser, err := transaction.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if executingUser.Role != UserRoleAdmin || !executingUser.IsServiceUser {
|
if executingUser.Role != types.UserRoleAdmin || !executingUser.IsServiceUser {
|
||||||
return status.Errorf(status.PermissionDenied, "only service users with admin power can delete integration group")
|
return status.Errorf(status.PermissionDenied, "only service users with admin power can delete integration group")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -429,8 +498,8 @@ func validateDeleteGroup(ctx context.Context, transaction Store, group *nbgroup.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// checkGroupLinkedToSettings verifies if a group is linked to any settings in the account.
|
// checkGroupLinkedToSettings verifies if a group is linked to any settings in the account.
|
||||||
func checkGroupLinkedToSettings(ctx context.Context, transaction Store, group *nbgroup.Group) error {
|
func checkGroupLinkedToSettings(ctx context.Context, transaction store.Store, group *types.Group) error {
|
||||||
dnsSettings, err := transaction.GetAccountDNSSettings(ctx, LockingStrengthShare, group.AccountID)
|
dnsSettings, err := transaction.GetAccountDNSSettings(ctx, store.LockingStrengthShare, group.AccountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -439,7 +508,7 @@ func checkGroupLinkedToSettings(ctx context.Context, transaction Store, group *n
|
|||||||
return &GroupLinkError{"disabled DNS management groups", group.Name}
|
return &GroupLinkError{"disabled DNS management groups", group.Name}
|
||||||
}
|
}
|
||||||
|
|
||||||
settings, err := transaction.GetAccountSettings(ctx, LockingStrengthShare, group.AccountID)
|
settings, err := transaction.GetAccountSettings(ctx, store.LockingStrengthShare, group.AccountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -452,8 +521,8 @@ func checkGroupLinkedToSettings(ctx context.Context, transaction Store, group *n
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isGroupLinkedToRoute checks if a group is linked to any route in the account.
|
// isGroupLinkedToRoute checks if a group is linked to any route in the account.
|
||||||
func isGroupLinkedToRoute(ctx context.Context, transaction Store, accountID string, groupID string) (bool, *route.Route) {
|
func isGroupLinkedToRoute(ctx context.Context, transaction store.Store, accountID string, groupID string) (bool, *route.Route) {
|
||||||
routes, err := transaction.GetAccountRoutes(ctx, LockingStrengthShare, accountID)
|
routes, err := transaction.GetAccountRoutes(ctx, store.LockingStrengthShare, accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithContext(ctx).Errorf("error retrieving routes while checking group linkage: %v", err)
|
log.WithContext(ctx).Errorf("error retrieving routes while checking group linkage: %v", err)
|
||||||
return false, nil
|
return false, nil
|
||||||
@ -469,8 +538,8 @@ func isGroupLinkedToRoute(ctx context.Context, transaction Store, accountID stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isGroupLinkedToPolicy checks if a group is linked to any policy in the account.
|
// isGroupLinkedToPolicy checks if a group is linked to any policy in the account.
|
||||||
func isGroupLinkedToPolicy(ctx context.Context, transaction Store, accountID string, groupID string) (bool, *Policy) {
|
func isGroupLinkedToPolicy(ctx context.Context, transaction store.Store, accountID string, groupID string) (bool, *types.Policy) {
|
||||||
policies, err := transaction.GetAccountPolicies(ctx, LockingStrengthShare, accountID)
|
policies, err := transaction.GetAccountPolicies(ctx, store.LockingStrengthShare, accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithContext(ctx).Errorf("error retrieving policies while checking group linkage: %v", err)
|
log.WithContext(ctx).Errorf("error retrieving policies while checking group linkage: %v", err)
|
||||||
return false, nil
|
return false, nil
|
||||||
@ -487,8 +556,8 @@ func isGroupLinkedToPolicy(ctx context.Context, transaction Store, accountID str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isGroupLinkedToDns checks if a group is linked to any nameserver group in the account.
|
// isGroupLinkedToDns checks if a group is linked to any nameserver group in the account.
|
||||||
func isGroupLinkedToDns(ctx context.Context, transaction Store, accountID string, groupID string) (bool, *nbdns.NameServerGroup) {
|
func isGroupLinkedToDns(ctx context.Context, transaction store.Store, accountID string, groupID string) (bool, *nbdns.NameServerGroup) {
|
||||||
nameServerGroups, err := transaction.GetAccountNameServerGroups(ctx, LockingStrengthShare, accountID)
|
nameServerGroups, err := transaction.GetAccountNameServerGroups(ctx, store.LockingStrengthShare, accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithContext(ctx).Errorf("error retrieving name server groups while checking group linkage: %v", err)
|
log.WithContext(ctx).Errorf("error retrieving name server groups while checking group linkage: %v", err)
|
||||||
return false, nil
|
return false, nil
|
||||||
@ -506,8 +575,8 @@ func isGroupLinkedToDns(ctx context.Context, transaction Store, accountID string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isGroupLinkedToSetupKey checks if a group is linked to any setup key in the account.
|
// isGroupLinkedToSetupKey checks if a group is linked to any setup key in the account.
|
||||||
func isGroupLinkedToSetupKey(ctx context.Context, transaction Store, accountID string, groupID string) (bool, *SetupKey) {
|
func isGroupLinkedToSetupKey(ctx context.Context, transaction store.Store, accountID string, groupID string) (bool, *types.SetupKey) {
|
||||||
setupKeys, err := transaction.GetAccountSetupKeys(ctx, LockingStrengthShare, accountID)
|
setupKeys, err := transaction.GetAccountSetupKeys(ctx, store.LockingStrengthShare, accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithContext(ctx).Errorf("error retrieving setup keys while checking group linkage: %v", err)
|
log.WithContext(ctx).Errorf("error retrieving setup keys while checking group linkage: %v", err)
|
||||||
return false, nil
|
return false, nil
|
||||||
@ -522,8 +591,8 @@ func isGroupLinkedToSetupKey(ctx context.Context, transaction Store, accountID s
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isGroupLinkedToUser checks if a group is linked to any user in the account.
|
// isGroupLinkedToUser checks if a group is linked to any user in the account.
|
||||||
func isGroupLinkedToUser(ctx context.Context, transaction Store, accountID string, groupID string) (bool, *User) {
|
func isGroupLinkedToUser(ctx context.Context, transaction store.Store, accountID string, groupID string) (bool, *types.User) {
|
||||||
users, err := transaction.GetAccountUsers(ctx, LockingStrengthShare, accountID)
|
users, err := transaction.GetAccountUsers(ctx, store.LockingStrengthShare, accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithContext(ctx).Errorf("error retrieving users while checking group linkage: %v", err)
|
log.WithContext(ctx).Errorf("error retrieving users while checking group linkage: %v", err)
|
||||||
return false, nil
|
return false, nil
|
||||||
@ -538,12 +607,12 @@ func isGroupLinkedToUser(ctx context.Context, transaction Store, accountID strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// areGroupChangesAffectPeers checks if any changes to the specified groups will affect peers.
|
// areGroupChangesAffectPeers checks if any changes to the specified groups will affect peers.
|
||||||
func areGroupChangesAffectPeers(ctx context.Context, transaction Store, accountID string, groupIDs []string) (bool, error) {
|
func areGroupChangesAffectPeers(ctx context.Context, transaction store.Store, accountID string, groupIDs []string) (bool, error) {
|
||||||
if len(groupIDs) == 0 {
|
if len(groupIDs) == 0 {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
dnsSettings, err := transaction.GetAccountDNSSettings(ctx, LockingStrengthShare, accountID)
|
dnsSettings, err := transaction.GetAccountDNSSettings(ctx, store.LockingStrengthShare, accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@ -566,7 +635,7 @@ func areGroupChangesAffectPeers(ctx context.Context, transaction Store, accountI
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *DefaultAccountManager) anyGroupHasPeers(account *Account, groupIDs []string) bool {
|
func (am *DefaultAccountManager) anyGroupHasPeers(account *types.Account, groupIDs []string) bool {
|
||||||
for _, groupID := range groupIDs {
|
for _, groupID := range groupIDs {
|
||||||
if group, exists := account.Groups[groupID]; exists && group.HasPeers() {
|
if group, exists := account.Groups[groupID]; exists && group.HasPeers() {
|
||||||
return true
|
return true
|
||||||
@ -575,15 +644,15 @@ func (am *DefaultAccountManager) anyGroupHasPeers(account *Account, groupIDs []s
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// anyGroupHasPeers checks if any of the given groups in the account have peers.
|
// anyGroupHasPeersOrResources checks if any of the given groups in the account have peers or resources.
|
||||||
func anyGroupHasPeers(ctx context.Context, transaction Store, accountID string, groupIDs []string) (bool, error) {
|
func anyGroupHasPeersOrResources(ctx context.Context, transaction store.Store, accountID string, groupIDs []string) (bool, error) {
|
||||||
groups, err := transaction.GetGroupsByIDs(ctx, LockingStrengthShare, accountID, groupIDs)
|
groups, err := transaction.GetGroupsByIDs(ctx, store.LockingStrengthShare, accountID, groupIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
if group.HasPeers() {
|
if group.HasPeers() || group.HasResources() {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,8 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
nbgroup "github.com/netbirdio/netbird/management/server/group"
|
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,22 +32,22 @@ func TestDefaultAccountManager_CreateGroup(t *testing.T) {
|
|||||||
t.Error("failed to init testing account")
|
t.Error("failed to init testing account")
|
||||||
}
|
}
|
||||||
for _, group := range account.Groups {
|
for _, group := range account.Groups {
|
||||||
group.Issued = nbgroup.GroupIssuedIntegration
|
group.Issued = types.GroupIssuedIntegration
|
||||||
err = am.SaveGroup(context.Background(), account.Id, groupAdminUserID, group)
|
err = am.SaveGroup(context.Background(), account.Id, groupAdminUserID, group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("should allow to create %s groups", nbgroup.GroupIssuedIntegration)
|
t.Errorf("should allow to create %s groups", types.GroupIssuedIntegration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, group := range account.Groups {
|
for _, group := range account.Groups {
|
||||||
group.Issued = nbgroup.GroupIssuedJWT
|
group.Issued = types.GroupIssuedJWT
|
||||||
err = am.SaveGroup(context.Background(), account.Id, groupAdminUserID, group)
|
err = am.SaveGroup(context.Background(), account.Id, groupAdminUserID, group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("should allow to create %s groups", nbgroup.GroupIssuedJWT)
|
t.Errorf("should allow to create %s groups", types.GroupIssuedJWT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, group := range account.Groups {
|
for _, group := range account.Groups {
|
||||||
group.Issued = nbgroup.GroupIssuedAPI
|
group.Issued = types.GroupIssuedAPI
|
||||||
group.ID = ""
|
group.ID = ""
|
||||||
err = am.SaveGroup(context.Background(), account.Id, groupAdminUserID, group)
|
err = am.SaveGroup(context.Background(), account.Id, groupAdminUserID, group)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -145,13 +145,13 @@ func TestDefaultAccountManager_DeleteGroups(t *testing.T) {
|
|||||||
manager, account, err := initTestGroupAccount(am)
|
manager, account, err := initTestGroupAccount(am)
|
||||||
assert.NoError(t, err, "Failed to init testing account")
|
assert.NoError(t, err, "Failed to init testing account")
|
||||||
|
|
||||||
groups := make([]*nbgroup.Group, 10)
|
groups := make([]*types.Group, 10)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
groups[i] = &nbgroup.Group{
|
groups[i] = &types.Group{
|
||||||
ID: fmt.Sprintf("group-%d", i+1),
|
ID: fmt.Sprintf("group-%d", i+1),
|
||||||
AccountID: account.Id,
|
AccountID: account.Id,
|
||||||
Name: fmt.Sprintf("group-%d", i+1),
|
Name: fmt.Sprintf("group-%d", i+1),
|
||||||
Issued: nbgroup.GroupIssuedAPI,
|
Issued: types.GroupIssuedAPI,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,63 +267,63 @@ func TestDefaultAccountManager_DeleteGroups(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func initTestGroupAccount(am *DefaultAccountManager) (*DefaultAccountManager, *Account, error) {
|
func initTestGroupAccount(am *DefaultAccountManager) (*DefaultAccountManager, *types.Account, error) {
|
||||||
accountID := "testingAcc"
|
accountID := "testingAcc"
|
||||||
domain := "example.com"
|
domain := "example.com"
|
||||||
|
|
||||||
groupForRoute := &nbgroup.Group{
|
groupForRoute := &types.Group{
|
||||||
ID: "grp-for-route",
|
ID: "grp-for-route",
|
||||||
AccountID: "account-id",
|
AccountID: "account-id",
|
||||||
Name: "Group for route",
|
Name: "Group for route",
|
||||||
Issued: nbgroup.GroupIssuedAPI,
|
Issued: types.GroupIssuedAPI,
|
||||||
Peers: make([]string, 0),
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
groupForRoute2 := &nbgroup.Group{
|
groupForRoute2 := &types.Group{
|
||||||
ID: "grp-for-route2",
|
ID: "grp-for-route2",
|
||||||
AccountID: "account-id",
|
AccountID: "account-id",
|
||||||
Name: "Group for route",
|
Name: "Group for route",
|
||||||
Issued: nbgroup.GroupIssuedAPI,
|
Issued: types.GroupIssuedAPI,
|
||||||
Peers: make([]string, 0),
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
groupForNameServerGroups := &nbgroup.Group{
|
groupForNameServerGroups := &types.Group{
|
||||||
ID: "grp-for-name-server-grp",
|
ID: "grp-for-name-server-grp",
|
||||||
AccountID: "account-id",
|
AccountID: "account-id",
|
||||||
Name: "Group for name server groups",
|
Name: "Group for name server groups",
|
||||||
Issued: nbgroup.GroupIssuedAPI,
|
Issued: types.GroupIssuedAPI,
|
||||||
Peers: make([]string, 0),
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
groupForPolicies := &nbgroup.Group{
|
groupForPolicies := &types.Group{
|
||||||
ID: "grp-for-policies",
|
ID: "grp-for-policies",
|
||||||
AccountID: "account-id",
|
AccountID: "account-id",
|
||||||
Name: "Group for policies",
|
Name: "Group for policies",
|
||||||
Issued: nbgroup.GroupIssuedAPI,
|
Issued: types.GroupIssuedAPI,
|
||||||
Peers: make([]string, 0),
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
groupForSetupKeys := &nbgroup.Group{
|
groupForSetupKeys := &types.Group{
|
||||||
ID: "grp-for-keys",
|
ID: "grp-for-keys",
|
||||||
AccountID: "account-id",
|
AccountID: "account-id",
|
||||||
Name: "Group for setup keys",
|
Name: "Group for setup keys",
|
||||||
Issued: nbgroup.GroupIssuedAPI,
|
Issued: types.GroupIssuedAPI,
|
||||||
Peers: make([]string, 0),
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
groupForUsers := &nbgroup.Group{
|
groupForUsers := &types.Group{
|
||||||
ID: "grp-for-users",
|
ID: "grp-for-users",
|
||||||
AccountID: "account-id",
|
AccountID: "account-id",
|
||||||
Name: "Group for users",
|
Name: "Group for users",
|
||||||
Issued: nbgroup.GroupIssuedAPI,
|
Issued: types.GroupIssuedAPI,
|
||||||
Peers: make([]string, 0),
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
groupForIntegration := &nbgroup.Group{
|
groupForIntegration := &types.Group{
|
||||||
ID: "grp-for-integration",
|
ID: "grp-for-integration",
|
||||||
AccountID: "account-id",
|
AccountID: "account-id",
|
||||||
Name: "Group for users integration",
|
Name: "Group for users integration",
|
||||||
Issued: nbgroup.GroupIssuedIntegration,
|
Issued: types.GroupIssuedIntegration,
|
||||||
Peers: make([]string, 0),
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,9 +342,9 @@ func initTestGroupAccount(am *DefaultAccountManager) (*DefaultAccountManager, *A
|
|||||||
Groups: []string{groupForNameServerGroups.ID},
|
Groups: []string{groupForNameServerGroups.ID},
|
||||||
}
|
}
|
||||||
|
|
||||||
policy := &Policy{
|
policy := &types.Policy{
|
||||||
ID: "example policy",
|
ID: "example policy",
|
||||||
Rules: []*PolicyRule{
|
Rules: []*types.PolicyRule{
|
||||||
{
|
{
|
||||||
ID: "example policy rule",
|
ID: "example policy rule",
|
||||||
Destinations: []string{groupForPolicies.ID},
|
Destinations: []string{groupForPolicies.ID},
|
||||||
@ -352,12 +352,12 @@ func initTestGroupAccount(am *DefaultAccountManager) (*DefaultAccountManager, *A
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
setupKey := &SetupKey{
|
setupKey := &types.SetupKey{
|
||||||
Id: "example setup key",
|
Id: "example setup key",
|
||||||
AutoGroups: []string{groupForSetupKeys.ID},
|
AutoGroups: []string{groupForSetupKeys.ID},
|
||||||
}
|
}
|
||||||
|
|
||||||
user := &User{
|
user := &types.User{
|
||||||
Id: "example user",
|
Id: "example user",
|
||||||
AutoGroups: []string{groupForUsers.ID},
|
AutoGroups: []string{groupForUsers.ID},
|
||||||
}
|
}
|
||||||
@ -392,7 +392,7 @@ func initTestGroupAccount(am *DefaultAccountManager) (*DefaultAccountManager, *A
|
|||||||
func TestGroupAccountPeersUpdate(t *testing.T) {
|
func TestGroupAccountPeersUpdate(t *testing.T) {
|
||||||
manager, account, peer1, peer2, peer3 := setupNetworkMapTest(t)
|
manager, account, peer1, peer2, peer3 := setupNetworkMapTest(t)
|
||||||
|
|
||||||
err := manager.SaveGroups(context.Background(), account.Id, userID, []*nbgroup.Group{
|
err := manager.SaveGroups(context.Background(), account.Id, userID, []*types.Group{
|
||||||
{
|
{
|
||||||
ID: "groupA",
|
ID: "groupA",
|
||||||
Name: "GroupA",
|
Name: "GroupA",
|
||||||
@ -429,7 +429,7 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
|||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := manager.SaveGroup(context.Background(), account.Id, userID, &nbgroup.Group{
|
err := manager.SaveGroup(context.Background(), account.Id, userID, &types.Group{
|
||||||
ID: "groupB",
|
ID: "groupB",
|
||||||
Name: "GroupB",
|
Name: "GroupB",
|
||||||
Peers: []string{peer1.ID, peer2.ID},
|
Peers: []string{peer1.ID, peer2.ID},
|
||||||
@ -500,15 +500,15 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// adding a group to policy
|
// adding a group to policy
|
||||||
_, err = manager.SavePolicy(context.Background(), account.Id, userID, &Policy{
|
_, err = manager.SavePolicy(context.Background(), account.Id, userID, &types.Policy{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Rules: []*PolicyRule{
|
Rules: []*types.PolicyRule{
|
||||||
{
|
{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Sources: []string{"groupA"},
|
Sources: []string{"groupA"},
|
||||||
Destinations: []string{"groupA"},
|
Destinations: []string{"groupA"},
|
||||||
Bidirectional: true,
|
Bidirectional: true,
|
||||||
Action: PolicyTrafficActionAccept,
|
Action: types.PolicyTrafficActionAccept,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -522,7 +522,7 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
|||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := manager.SaveGroup(context.Background(), account.Id, userID, &nbgroup.Group{
|
err := manager.SaveGroup(context.Background(), account.Id, userID, &types.Group{
|
||||||
ID: "groupA",
|
ID: "groupA",
|
||||||
Name: "GroupA",
|
Name: "GroupA",
|
||||||
Peers: []string{peer1.ID, peer2.ID},
|
Peers: []string{peer1.ID, peer2.ID},
|
||||||
@ -591,7 +591,7 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
|||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := manager.SaveGroup(context.Background(), account.Id, userID, &nbgroup.Group{
|
err := manager.SaveGroup(context.Background(), account.Id, userID, &types.Group{
|
||||||
ID: "groupC",
|
ID: "groupC",
|
||||||
Name: "GroupC",
|
Name: "GroupC",
|
||||||
Peers: []string{peer1.ID, peer3.ID},
|
Peers: []string{peer1.ID, peer3.ID},
|
||||||
@ -632,7 +632,7 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
|||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = manager.SaveGroup(context.Background(), account.Id, userID, &nbgroup.Group{
|
err = manager.SaveGroup(context.Background(), account.Id, userID, &types.Group{
|
||||||
ID: "groupA",
|
ID: "groupA",
|
||||||
Name: "GroupA",
|
Name: "GroupA",
|
||||||
Peers: []string{peer1.ID, peer2.ID, peer3.ID},
|
Peers: []string{peer1.ID, peer2.ID, peer3.ID},
|
||||||
@ -648,7 +648,7 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
|||||||
|
|
||||||
// Saving a group linked to dns settings should update account peers and send peer update
|
// Saving a group linked to dns settings should update account peers and send peer update
|
||||||
t.Run("saving group linked to dns settings", func(t *testing.T) {
|
t.Run("saving group linked to dns settings", func(t *testing.T) {
|
||||||
err := manager.SaveDNSSettings(context.Background(), account.Id, userID, &DNSSettings{
|
err := manager.SaveDNSSettings(context.Background(), account.Id, userID, &types.DNSSettings{
|
||||||
DisabledManagementGroups: []string{"groupD"},
|
DisabledManagementGroups: []string{"groupD"},
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
@ -659,7 +659,7 @@ func TestGroupAccountPeersUpdate(t *testing.T) {
|
|||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = manager.SaveGroup(context.Background(), account.Id, userID, &nbgroup.Group{
|
err = manager.SaveGroup(context.Background(), account.Id, userID, &types.Group{
|
||||||
ID: "groupD",
|
ID: "groupD",
|
||||||
Name: "GroupD",
|
Name: "GroupD",
|
||||||
Peers: []string{peer1.ID},
|
Peers: []string{peer1.ID},
|
||||||
|
196
management/server/groups/manager.go
Normal file
196
management/server/groups/manager.go
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
package groups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
s "github.com/netbirdio/netbird/management/server"
|
||||||
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
|
"github.com/netbirdio/netbird/management/server/permissions"
|
||||||
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Manager interface {
|
||||||
|
GetAllGroups(ctx context.Context, accountID, userID string) (map[string]*types.Group, error)
|
||||||
|
GetResourceGroupsInTransaction(ctx context.Context, transaction store.Store, lockingStrength store.LockingStrength, accountID, resourceID string) ([]*types.Group, error)
|
||||||
|
AddResourceToGroup(ctx context.Context, accountID, userID, groupID string, resourceID *types.Resource) error
|
||||||
|
AddResourceToGroupInTransaction(ctx context.Context, transaction store.Store, accountID, userID, groupID string, resourceID *types.Resource) (func(), error)
|
||||||
|
RemoveResourceFromGroupInTransaction(ctx context.Context, transaction store.Store, accountID, userID, groupID, resourceID string) (func(), error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type managerImpl struct {
|
||||||
|
store store.Store
|
||||||
|
permissionsManager permissions.Manager
|
||||||
|
accountManager s.AccountManager
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockManager struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManager(store store.Store, permissionsManager permissions.Manager, accountManager s.AccountManager) Manager {
|
||||||
|
return &managerImpl{
|
||||||
|
store: store,
|
||||||
|
permissionsManager: permissionsManager,
|
||||||
|
accountManager: accountManager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerImpl) GetAllGroups(ctx context.Context, accountID, userID string) (map[string]*types.Group, error) {
|
||||||
|
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Groups, permissions.Read)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
groups, err := m.store.GetAccountGroups(ctx, store.LockingStrengthShare, accountID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error getting account groups: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
groupsMap := make(map[string]*types.Group)
|
||||||
|
for _, group := range groups {
|
||||||
|
groupsMap[group.ID] = group
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupsMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerImpl) AddResourceToGroup(ctx context.Context, accountID, userID, groupID string, resource *types.Resource) error {
|
||||||
|
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, permissions.Groups, permissions.Write)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
event, err := m.AddResourceToGroupInTransaction(ctx, m.store, accountID, userID, groupID, resource)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error adding resource to group: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
event()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerImpl) AddResourceToGroupInTransaction(ctx context.Context, transaction store.Store, accountID, userID, groupID string, resource *types.Resource) (func(), error) {
|
||||||
|
err := transaction.AddResourceToGroup(ctx, accountID, groupID, resource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error adding resource to group: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
group, err := transaction.GetGroupByID(ctx, store.LockingStrengthShare, accountID, groupID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error getting group: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: at some point, this will need to become a switch statement
|
||||||
|
networkResource, err := transaction.GetNetworkResourceByID(ctx, store.LockingStrengthShare, accountID, resource.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error getting network resource: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
event := func() {
|
||||||
|
m.accountManager.StoreEvent(ctx, userID, groupID, accountID, activity.ResourceAddedToGroup, group.EventMetaResource(networkResource))
|
||||||
|
}
|
||||||
|
|
||||||
|
return event, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerImpl) RemoveResourceFromGroupInTransaction(ctx context.Context, transaction store.Store, accountID, userID, groupID, resourceID string) (func(), error) {
|
||||||
|
err := transaction.RemoveResourceFromGroup(ctx, accountID, groupID, resourceID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error removing resource from group: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
group, err := transaction.GetGroupByID(ctx, store.LockingStrengthShare, accountID, groupID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error getting group: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: at some point, this will need to become a switch statement
|
||||||
|
networkResource, err := transaction.GetNetworkResourceByID(ctx, store.LockingStrengthShare, accountID, resourceID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error getting network resource: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
event := func() {
|
||||||
|
m.accountManager.StoreEvent(ctx, userID, groupID, accountID, activity.ResourceRemovedFromGroup, group.EventMetaResource(networkResource))
|
||||||
|
}
|
||||||
|
|
||||||
|
return event, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *managerImpl) GetResourceGroupsInTransaction(ctx context.Context, transaction store.Store, lockingStrength store.LockingStrength, accountID, resourceID string) ([]*types.Group, error) {
|
||||||
|
return transaction.GetResourceGroups(ctx, lockingStrength, accountID, resourceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToGroupsInfo(groups map[string]*types.Group, id string) []api.GroupMinimum {
|
||||||
|
groupsInfo := []api.GroupMinimum{}
|
||||||
|
groupsChecked := make(map[string]struct{})
|
||||||
|
for _, group := range groups {
|
||||||
|
_, ok := groupsChecked[group.ID]
|
||||||
|
if ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
groupsChecked[group.ID] = struct{}{}
|
||||||
|
for _, pk := range group.Peers {
|
||||||
|
if pk == id {
|
||||||
|
info := api.GroupMinimum{
|
||||||
|
Id: group.ID,
|
||||||
|
Name: group.Name,
|
||||||
|
PeersCount: len(group.Peers),
|
||||||
|
ResourcesCount: len(group.Resources),
|
||||||
|
}
|
||||||
|
groupsInfo = append(groupsInfo, info)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, rk := range group.Resources {
|
||||||
|
if rk.ID == id {
|
||||||
|
info := api.GroupMinimum{
|
||||||
|
Id: group.ID,
|
||||||
|
Name: group.Name,
|
||||||
|
PeersCount: len(group.Peers),
|
||||||
|
ResourcesCount: len(group.Resources),
|
||||||
|
}
|
||||||
|
groupsInfo = append(groupsInfo, info)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return groupsInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockManager) GetAllGroups(ctx context.Context, accountID, userID string) (map[string]*types.Group, error) {
|
||||||
|
return map[string]*types.Group{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockManager) GetResourceGroupsInTransaction(ctx context.Context, transaction store.Store, lockingStrength store.LockingStrength, accountID, resourceID string) ([]*types.Group, error) {
|
||||||
|
return []*types.Group{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockManager) AddResourceToGroup(ctx context.Context, accountID, userID, groupID string, resourceID *types.Resource) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockManager) AddResourceToGroupInTransaction(ctx context.Context, transaction store.Store, accountID, userID, groupID string, resourceID *types.Resource) (func(), error) {
|
||||||
|
return func() {
|
||||||
|
// noop
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockManager) RemoveResourceFromGroupInTransaction(ctx context.Context, transaction store.Store, accountID, userID, groupID, resourceID string) (func(), error) {
|
||||||
|
return func() {
|
||||||
|
// noop
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManagerMock() Manager {
|
||||||
|
return &mockManager{}
|
||||||
|
}
|
@ -23,14 +23,17 @@ import (
|
|||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||||
"github.com/netbirdio/netbird/management/server/posture"
|
"github.com/netbirdio/netbird/management/server/posture"
|
||||||
|
"github.com/netbirdio/netbird/management/server/settings"
|
||||||
internalStatus "github.com/netbirdio/netbird/management/server/status"
|
internalStatus "github.com/netbirdio/netbird/management/server/status"
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GRPCServer an instance of a Management gRPC API server
|
// GRPCServer an instance of a Management gRPC API server
|
||||||
type GRPCServer struct {
|
type GRPCServer struct {
|
||||||
accountManager AccountManager
|
accountManager AccountManager
|
||||||
wgKey wgtypes.Key
|
settingsManager settings.Manager
|
||||||
|
wgKey wgtypes.Key
|
||||||
proto.UnimplementedManagementServiceServer
|
proto.UnimplementedManagementServiceServer
|
||||||
peersUpdateManager *PeersUpdateManager
|
peersUpdateManager *PeersUpdateManager
|
||||||
config *Config
|
config *Config
|
||||||
@ -47,6 +50,7 @@ func NewServer(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
config *Config,
|
config *Config,
|
||||||
accountManager AccountManager,
|
accountManager AccountManager,
|
||||||
|
settingsManager settings.Manager,
|
||||||
peersUpdateManager *PeersUpdateManager,
|
peersUpdateManager *PeersUpdateManager,
|
||||||
secretsManager SecretsManager,
|
secretsManager SecretsManager,
|
||||||
appMetrics telemetry.AppMetrics,
|
appMetrics telemetry.AppMetrics,
|
||||||
@ -99,6 +103,7 @@ func NewServer(
|
|||||||
// peerKey -> event channel
|
// peerKey -> event channel
|
||||||
peersUpdateManager: peersUpdateManager,
|
peersUpdateManager: peersUpdateManager,
|
||||||
accountManager: accountManager,
|
accountManager: accountManager,
|
||||||
|
settingsManager: settingsManager,
|
||||||
config: config,
|
config: config,
|
||||||
secretsManager: secretsManager,
|
secretsManager: secretsManager,
|
||||||
jwtValidator: jwtValidator,
|
jwtValidator: jwtValidator,
|
||||||
@ -483,7 +488,7 @@ func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*p
|
|||||||
// if peer has reached this point then it has logged in
|
// if peer has reached this point then it has logged in
|
||||||
loginResp := &proto.LoginResponse{
|
loginResp := &proto.LoginResponse{
|
||||||
WiretrusteeConfig: toWiretrusteeConfig(s.config, nil, relayToken),
|
WiretrusteeConfig: toWiretrusteeConfig(s.config, nil, relayToken),
|
||||||
PeerConfig: toPeerConfig(peer, netMap.Network, s.accountManager.GetDNSDomain()),
|
PeerConfig: toPeerConfig(peer, netMap.Network, s.accountManager.GetDNSDomain(), false),
|
||||||
Checks: toProtocolChecks(ctx, postureChecks),
|
Checks: toProtocolChecks(ctx, postureChecks),
|
||||||
}
|
}
|
||||||
encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, loginResp)
|
encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, loginResp)
|
||||||
@ -599,20 +604,21 @@ func toWiretrusteeConfig(config *Config, turnCredentials *Token, relayToken *Tok
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toPeerConfig(peer *nbpeer.Peer, network *Network, dnsName string) *proto.PeerConfig {
|
func toPeerConfig(peer *nbpeer.Peer, network *types.Network, dnsName string, dnsResolutionOnRoutingPeerEnabled bool) *proto.PeerConfig {
|
||||||
netmask, _ := network.Net.Mask.Size()
|
netmask, _ := network.Net.Mask.Size()
|
||||||
fqdn := peer.FQDN(dnsName)
|
fqdn := peer.FQDN(dnsName)
|
||||||
return &proto.PeerConfig{
|
return &proto.PeerConfig{
|
||||||
Address: fmt.Sprintf("%s/%d", peer.IP.String(), netmask), // take it from the network
|
Address: fmt.Sprintf("%s/%d", peer.IP.String(), netmask), // take it from the network
|
||||||
SshConfig: &proto.SSHConfig{SshEnabled: peer.SSHEnabled},
|
SshConfig: &proto.SSHConfig{SshEnabled: peer.SSHEnabled},
|
||||||
Fqdn: fqdn,
|
Fqdn: fqdn,
|
||||||
|
RoutingPeerDnsResolutionEnabled: dnsResolutionOnRoutingPeerEnabled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toSyncResponse(ctx context.Context, config *Config, peer *nbpeer.Peer, turnCredentials *Token, relayCredentials *Token, networkMap *NetworkMap, dnsName string, checks []*posture.Checks, dnsCache *DNSConfigCache) *proto.SyncResponse {
|
func toSyncResponse(ctx context.Context, config *Config, peer *nbpeer.Peer, turnCredentials *Token, relayCredentials *Token, networkMap *types.NetworkMap, dnsName string, checks []*posture.Checks, dnsCache *DNSConfigCache, dnsResolutionOnRoutingPeerEnbled bool) *proto.SyncResponse {
|
||||||
response := &proto.SyncResponse{
|
response := &proto.SyncResponse{
|
||||||
WiretrusteeConfig: toWiretrusteeConfig(config, turnCredentials, relayCredentials),
|
WiretrusteeConfig: toWiretrusteeConfig(config, turnCredentials, relayCredentials),
|
||||||
PeerConfig: toPeerConfig(peer, networkMap.Network, dnsName),
|
PeerConfig: toPeerConfig(peer, networkMap.Network, dnsName, dnsResolutionOnRoutingPeerEnbled),
|
||||||
NetworkMap: &proto.NetworkMap{
|
NetworkMap: &proto.NetworkMap{
|
||||||
Serial: networkMap.Network.CurrentSerial(),
|
Serial: networkMap.Network.CurrentSerial(),
|
||||||
Routes: toProtocolRoutes(networkMap.Routes),
|
Routes: toProtocolRoutes(networkMap.Routes),
|
||||||
@ -661,7 +667,7 @@ func (s *GRPCServer) IsHealthy(ctx context.Context, req *proto.Empty) (*proto.Em
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sendInitialSync sends initial proto.SyncResponse to the peer requesting synchronization
|
// sendInitialSync sends initial proto.SyncResponse to the peer requesting synchronization
|
||||||
func (s *GRPCServer) sendInitialSync(ctx context.Context, peerKey wgtypes.Key, peer *nbpeer.Peer, networkMap *NetworkMap, postureChecks []*posture.Checks, srv proto.ManagementService_SyncServer) error {
|
func (s *GRPCServer) sendInitialSync(ctx context.Context, peerKey wgtypes.Key, peer *nbpeer.Peer, networkMap *types.NetworkMap, postureChecks []*posture.Checks, srv proto.ManagementService_SyncServer) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
var turnToken *Token
|
var turnToken *Token
|
||||||
@ -680,7 +686,12 @@ func (s *GRPCServer) sendInitialSync(ctx context.Context, peerKey wgtypes.Key, p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plainResp := toSyncResponse(ctx, s.config, peer, turnToken, relayToken, networkMap, s.accountManager.GetDNSDomain(), postureChecks, nil)
|
settings, err := s.settingsManager.GetSettings(ctx, peer.AccountID, peer.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return status.Errorf(codes.Internal, "error handling request")
|
||||||
|
}
|
||||||
|
|
||||||
|
plainResp := toSyncResponse(ctx, s.config, peer, turnToken, relayToken, networkMap, s.accountManager.GetDNSDomain(), postureChecks, nil, settings.RoutingPeerDNSResolutionEnabled)
|
||||||
|
|
||||||
encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, plainResp)
|
encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, plainResp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -84,6 +84,10 @@ components:
|
|||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
example: Administrators
|
example: Administrators
|
||||||
|
routing_peer_dns_resolution_enabled:
|
||||||
|
description: Enables or disables DNS resolution on the routing peers
|
||||||
|
type: boolean
|
||||||
|
example: true
|
||||||
extra:
|
extra:
|
||||||
$ref: '#/components/schemas/AccountExtraSettings'
|
$ref: '#/components/schemas/AccountExtraSettings'
|
||||||
required:
|
required:
|
||||||
@ -668,6 +672,10 @@ components:
|
|||||||
description: Count of peers associated to the group
|
description: Count of peers associated to the group
|
||||||
type: integer
|
type: integer
|
||||||
example: 2
|
example: 2
|
||||||
|
resources_count:
|
||||||
|
description: Count of resources associated to the group
|
||||||
|
type: integer
|
||||||
|
example: 5
|
||||||
issued:
|
issued:
|
||||||
description: How the group was issued (api, integration, jwt)
|
description: How the group was issued (api, integration, jwt)
|
||||||
type: string
|
type: string
|
||||||
@ -677,6 +685,7 @@ components:
|
|||||||
- id
|
- id
|
||||||
- name
|
- name
|
||||||
- peers_count
|
- peers_count
|
||||||
|
- resources_count
|
||||||
GroupRequest:
|
GroupRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -690,6 +699,10 @@ components:
|
|||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
example: "ch8i4ug6lnn4g9hqv7m1"
|
example: "ch8i4ug6lnn4g9hqv7m1"
|
||||||
|
resources:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Resource'
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
Group:
|
Group:
|
||||||
@ -702,8 +715,13 @@ components:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/PeerMinimum'
|
$ref: '#/components/schemas/PeerMinimum'
|
||||||
|
resources:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Resource'
|
||||||
required:
|
required:
|
||||||
- peers
|
- peers
|
||||||
|
- resources
|
||||||
PolicyRuleMinimum:
|
PolicyRuleMinimum:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -782,15 +800,18 @@ components:
|
|||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
example: "ch8i4ug6lnn4g9hqv797"
|
example: "ch8i4ug6lnn4g9hqv797"
|
||||||
|
sourceResource:
|
||||||
|
description: Policy rule source resource that the rule is applied to
|
||||||
|
$ref: '#/components/schemas/Resource'
|
||||||
destinations:
|
destinations:
|
||||||
description: Policy rule destination group IDs
|
description: Policy rule destination group IDs
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
example: "ch8i4ug6lnn4g9h7v7m0"
|
example: "ch8i4ug6lnn4g9h7v7m0"
|
||||||
required:
|
destinationResource:
|
||||||
- sources
|
description: Policy rule destination resource that the rule is applied to
|
||||||
- destinations
|
$ref: '#/components/schemas/Resource'
|
||||||
PolicyRule:
|
PolicyRule:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/PolicyRuleMinimum'
|
- $ref: '#/components/schemas/PolicyRuleMinimum'
|
||||||
@ -801,14 +822,17 @@ components:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/GroupMinimum'
|
$ref: '#/components/schemas/GroupMinimum'
|
||||||
|
sourceResource:
|
||||||
|
description: Policy rule source resource that the rule is applied to
|
||||||
|
$ref: '#/components/schemas/Resource'
|
||||||
destinations:
|
destinations:
|
||||||
description: Policy rule destination group IDs
|
description: Policy rule destination group IDs
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/GroupMinimum'
|
$ref: '#/components/schemas/GroupMinimum'
|
||||||
required:
|
destinationResource:
|
||||||
- sources
|
description: Policy rule destination resource that the rule is applied to
|
||||||
- destinations
|
$ref: '#/components/schemas/Resource'
|
||||||
PolicyMinimum:
|
PolicyMinimum:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -1176,6 +1200,171 @@ components:
|
|||||||
- id
|
- id
|
||||||
- network_type
|
- network_type
|
||||||
- $ref: '#/components/schemas/RouteRequest'
|
- $ref: '#/components/schemas/RouteRequest'
|
||||||
|
Resource:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
description: ID of the resource
|
||||||
|
type: string
|
||||||
|
example: chacdk86lnnboviihd7g
|
||||||
|
type:
|
||||||
|
description: Type of the resource
|
||||||
|
$ref: '#/components/schemas/ResourceType'
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- type
|
||||||
|
ResourceType:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/NetworkResourceType'
|
||||||
|
- type: string
|
||||||
|
example: host
|
||||||
|
NetworkRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
description: Network name
|
||||||
|
type: string
|
||||||
|
example: Remote Network 1
|
||||||
|
description:
|
||||||
|
description: Network description
|
||||||
|
type: string
|
||||||
|
example: A remote network that needs to be accessed
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
Network:
|
||||||
|
allOf:
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
description: Network ID
|
||||||
|
type: string
|
||||||
|
example: chacdk86lnnboviihd7g
|
||||||
|
routers:
|
||||||
|
description: List of router IDs associated with the network
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: ch8i4ug6lnn4g9hqv7m0
|
||||||
|
routing_peers_count:
|
||||||
|
description: Count of routing peers associated with the network
|
||||||
|
type: integer
|
||||||
|
example: 2
|
||||||
|
resources:
|
||||||
|
description: List of network resource IDs associated with the network
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: ch8i4ug6lnn4g9hqv7m1
|
||||||
|
policies:
|
||||||
|
description: List of policy IDs associated with the network
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: ch8i4ug6lnn4g9hqv7m2
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- routers
|
||||||
|
- resources
|
||||||
|
- routing_peers_count
|
||||||
|
- policies
|
||||||
|
- $ref: '#/components/schemas/NetworkRequest'
|
||||||
|
NetworkResourceMinimum:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
description: Network resource name
|
||||||
|
type: string
|
||||||
|
example: Remote Resource 1
|
||||||
|
description:
|
||||||
|
description: Network resource description
|
||||||
|
type: string
|
||||||
|
example: A remote resource inside network 1
|
||||||
|
address:
|
||||||
|
description: Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or domains like example.com and *.example.com)
|
||||||
|
type: string
|
||||||
|
example: "1.1.1.1"
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- address
|
||||||
|
NetworkResourceRequest:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/NetworkResourceMinimum'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
groups:
|
||||||
|
description: Group IDs containing the resource
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: "chacdk86lnnboviihd70"
|
||||||
|
required:
|
||||||
|
- groups
|
||||||
|
- address
|
||||||
|
NetworkResource:
|
||||||
|
allOf:
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
description: Network Resource ID
|
||||||
|
type: string
|
||||||
|
example: chacdk86lnnboviihd7g
|
||||||
|
type:
|
||||||
|
$ref: '#/components/schemas/NetworkResourceType'
|
||||||
|
groups:
|
||||||
|
description: Groups that the resource belongs to
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/GroupMinimum'
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- type
|
||||||
|
- groups
|
||||||
|
- $ref: '#/components/schemas/NetworkResourceMinimum'
|
||||||
|
NetworkResourceType:
|
||||||
|
description: Network resource type based of the address
|
||||||
|
type: string
|
||||||
|
enum: [ "host", "subnet", "domain" ]
|
||||||
|
example: host
|
||||||
|
NetworkRouterRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
peer:
|
||||||
|
description: Peer Identifier associated with route. This property can not be set together with `peer_groups`
|
||||||
|
type: string
|
||||||
|
example: chacbco6lnnbn6cg5s91
|
||||||
|
peer_groups:
|
||||||
|
description: Peers Group Identifier associated with route. This property can not be set together with `peer`
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: chacbco6lnnbn6cg5s91
|
||||||
|
metric:
|
||||||
|
description: Route metric number. Lowest number has higher priority
|
||||||
|
type: integer
|
||||||
|
maximum: 9999
|
||||||
|
minimum: 1
|
||||||
|
example: 9999
|
||||||
|
masquerade:
|
||||||
|
description: Indicate if peer should masquerade traffic to this route's prefix
|
||||||
|
type: boolean
|
||||||
|
example: true
|
||||||
|
required:
|
||||||
|
# Only one property has to be set
|
||||||
|
#- peer
|
||||||
|
#- peer_groups
|
||||||
|
- metric
|
||||||
|
- masquerade
|
||||||
|
NetworkRouter:
|
||||||
|
allOf:
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
description: Network Router Id
|
||||||
|
type: string
|
||||||
|
example: chacdk86lnnboviihd7g
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- $ref: '#/components/schemas/NetworkRouterRequest'
|
||||||
Nameserver:
|
Nameserver:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -2460,6 +2649,502 @@ paths:
|
|||||||
"$ref": "#/components/responses/forbidden"
|
"$ref": "#/components/responses/forbidden"
|
||||||
'500':
|
'500':
|
||||||
"$ref": "#/components/responses/internal_error"
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
/api/networks:
|
||||||
|
get:
|
||||||
|
summary: List all Networks
|
||||||
|
description: Returns a list of all networks
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A JSON Array of Networks
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Network'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
post:
|
||||||
|
summary: Create a Network
|
||||||
|
description: Creates a Network
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
requestBody:
|
||||||
|
description: New Network request
|
||||||
|
content:
|
||||||
|
'application/json':
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/NetworkRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A Network Object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Network'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
/api/networks/{networkId}:
|
||||||
|
get:
|
||||||
|
summary: Retrieve a Network
|
||||||
|
description: Get information about a Network
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: networkId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A Network object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Network'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
put:
|
||||||
|
summary: Update a Network
|
||||||
|
description: Update/Replace a Network
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: networkId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network
|
||||||
|
requestBody:
|
||||||
|
description: Update Network request
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/NetworkRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A Network object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Network'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
delete:
|
||||||
|
summary: Delete a Network
|
||||||
|
description: Delete a network
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: networkId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Delete status code
|
||||||
|
content: { }
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
/api/networks/{networkId}/resources:
|
||||||
|
get:
|
||||||
|
summary: List all Network Resources
|
||||||
|
description: Returns a list of all resources in a network
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: networkId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A JSON Array of Resources
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/NetworkResource'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
post:
|
||||||
|
summary: Create a Network Resource
|
||||||
|
description: Creates a Network Resource
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: networkId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network
|
||||||
|
requestBody:
|
||||||
|
description: New Network Resource request
|
||||||
|
content:
|
||||||
|
'application/json':
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/NetworkResourceRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A Network Resource Object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/NetworkResource'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
/api/networks/{networkId}/resources/{resourceId}:
|
||||||
|
get:
|
||||||
|
summary: Retrieve a Network Resource
|
||||||
|
description: Get information about a Network Resource
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: networkId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network
|
||||||
|
- in: path
|
||||||
|
name: resourceId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network resource
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A Network Resource object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/NetworkResource'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
put:
|
||||||
|
summary: Update a Network Resource
|
||||||
|
description: Update a Network Resource
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: networkId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network
|
||||||
|
- in: path
|
||||||
|
name: resourceId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a resource
|
||||||
|
requestBody:
|
||||||
|
description: Update Network Resource request
|
||||||
|
content:
|
||||||
|
'application/json':
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/NetworkResourceRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A Network Resource object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/NetworkResource'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
delete:
|
||||||
|
summary: Delete a Network Resource
|
||||||
|
description: Delete a network resource
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: networkId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network
|
||||||
|
- in: path
|
||||||
|
name: resourceId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network resource
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Delete status code
|
||||||
|
content: { }
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
/api/networks/{networkId}/routers:
|
||||||
|
get:
|
||||||
|
summary: List all Network Routers
|
||||||
|
description: Returns a list of all routers in a network
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: networkId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A JSON Array of Routers
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/NetworkRouter'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
post:
|
||||||
|
summary: Create a Network Router
|
||||||
|
description: Creates a Network Router
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: networkId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network
|
||||||
|
requestBody:
|
||||||
|
description: New Network Router request
|
||||||
|
content:
|
||||||
|
'application/json':
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/NetworkRouterRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A Router Object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/NetworkRouter'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
/api/networks/{networkId}/routers/{routerId}:
|
||||||
|
get:
|
||||||
|
summary: Retrieve a Network Router
|
||||||
|
description: Get information about a Network Router
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: networkId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network
|
||||||
|
- in: path
|
||||||
|
name: routerId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a router
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A Router object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/NetworkRouter'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
put:
|
||||||
|
summary: Update a Network Router
|
||||||
|
description: Update a Network Router
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: networkId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network
|
||||||
|
- in: path
|
||||||
|
name: routerId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a router
|
||||||
|
requestBody:
|
||||||
|
description: Update Network Router request
|
||||||
|
content:
|
||||||
|
'application/json':
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/NetworkRouterRequest'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A Router object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/NetworkRouter'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
delete:
|
||||||
|
summary: Delete a Network Router
|
||||||
|
description: Delete a network router
|
||||||
|
tags: [ Networks ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: networkId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a network
|
||||||
|
- in: path
|
||||||
|
name: routerId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a router
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Delete status code
|
||||||
|
content: { }
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
/api/dns/nameservers:
|
/api/dns/nameservers:
|
||||||
get:
|
get:
|
||||||
summary: List all Nameserver Groups
|
summary: List all Nameserver Groups
|
||||||
|
@ -88,6 +88,13 @@ const (
|
|||||||
NameserverNsTypeUdp NameserverNsType = "udp"
|
NameserverNsTypeUdp NameserverNsType = "udp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Defines values for NetworkResourceType.
|
||||||
|
const (
|
||||||
|
NetworkResourceTypeDomain NetworkResourceType = "domain"
|
||||||
|
NetworkResourceTypeHost NetworkResourceType = "host"
|
||||||
|
NetworkResourceTypeSubnet NetworkResourceType = "subnet"
|
||||||
|
)
|
||||||
|
|
||||||
// Defines values for PeerNetworkRangeCheckAction.
|
// Defines values for PeerNetworkRangeCheckAction.
|
||||||
const (
|
const (
|
||||||
PeerNetworkRangeCheckActionAllow PeerNetworkRangeCheckAction = "allow"
|
PeerNetworkRangeCheckActionAllow PeerNetworkRangeCheckAction = "allow"
|
||||||
@ -136,6 +143,13 @@ const (
|
|||||||
PolicyRuleUpdateProtocolUdp PolicyRuleUpdateProtocol = "udp"
|
PolicyRuleUpdateProtocolUdp PolicyRuleUpdateProtocol = "udp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Defines values for ResourceType.
|
||||||
|
const (
|
||||||
|
ResourceTypeDomain ResourceType = "domain"
|
||||||
|
ResourceTypeHost ResourceType = "host"
|
||||||
|
ResourceTypeSubnet ResourceType = "subnet"
|
||||||
|
)
|
||||||
|
|
||||||
// Defines values for UserStatus.
|
// Defines values for UserStatus.
|
||||||
const (
|
const (
|
||||||
UserStatusActive UserStatus = "active"
|
UserStatusActive UserStatus = "active"
|
||||||
@ -234,6 +248,9 @@ type AccountSettings struct {
|
|||||||
|
|
||||||
// RegularUsersViewBlocked Allows blocking regular users from viewing parts of the system.
|
// RegularUsersViewBlocked Allows blocking regular users from viewing parts of the system.
|
||||||
RegularUsersViewBlocked bool `json:"regular_users_view_blocked"`
|
RegularUsersViewBlocked bool `json:"regular_users_view_blocked"`
|
||||||
|
|
||||||
|
// RoutingPeerDnsResolutionEnabled Enables or disables DNS resolution on the routing peers
|
||||||
|
RoutingPeerDnsResolutionEnabled *bool `json:"routing_peer_dns_resolution_enabled,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks List of objects that perform the actual checks
|
// Checks List of objects that perform the actual checks
|
||||||
@ -365,7 +382,11 @@ type Group struct {
|
|||||||
Peers []PeerMinimum `json:"peers"`
|
Peers []PeerMinimum `json:"peers"`
|
||||||
|
|
||||||
// PeersCount Count of peers associated to the group
|
// PeersCount Count of peers associated to the group
|
||||||
PeersCount int `json:"peers_count"`
|
PeersCount int `json:"peers_count"`
|
||||||
|
Resources []Resource `json:"resources"`
|
||||||
|
|
||||||
|
// ResourcesCount Count of resources associated to the group
|
||||||
|
ResourcesCount int `json:"resources_count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GroupIssued How the group was issued (api, integration, jwt)
|
// GroupIssued How the group was issued (api, integration, jwt)
|
||||||
@ -384,6 +405,9 @@ type GroupMinimum struct {
|
|||||||
|
|
||||||
// PeersCount Count of peers associated to the group
|
// PeersCount Count of peers associated to the group
|
||||||
PeersCount int `json:"peers_count"`
|
PeersCount int `json:"peers_count"`
|
||||||
|
|
||||||
|
// ResourcesCount Count of resources associated to the group
|
||||||
|
ResourcesCount int `json:"resources_count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GroupMinimumIssued How the group was issued (api, integration, jwt)
|
// GroupMinimumIssued How the group was issued (api, integration, jwt)
|
||||||
@ -395,7 +419,8 @@ type GroupRequest struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
// Peers List of peers ids
|
// Peers List of peers ids
|
||||||
Peers *[]string `json:"peers,omitempty"`
|
Peers *[]string `json:"peers,omitempty"`
|
||||||
|
Resources *[]Resource `json:"resources,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Location Describe geographical location information
|
// Location Describe geographical location information
|
||||||
@ -494,6 +519,123 @@ type NameserverGroupRequest struct {
|
|||||||
SearchDomainsEnabled bool `json:"search_domains_enabled"`
|
SearchDomainsEnabled bool `json:"search_domains_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Network defines model for Network.
|
||||||
|
type Network struct {
|
||||||
|
// Description Network description
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
|
||||||
|
// Id Network ID
|
||||||
|
Id string `json:"id"`
|
||||||
|
|
||||||
|
// Name Network name
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Policies List of policy IDs associated with the network
|
||||||
|
Policies []string `json:"policies"`
|
||||||
|
|
||||||
|
// Resources List of network resource IDs associated with the network
|
||||||
|
Resources []string `json:"resources"`
|
||||||
|
|
||||||
|
// Routers List of router IDs associated with the network
|
||||||
|
Routers []string `json:"routers"`
|
||||||
|
|
||||||
|
// RoutingPeersCount Count of routing peers associated with the network
|
||||||
|
RoutingPeersCount int `json:"routing_peers_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkRequest defines model for NetworkRequest.
|
||||||
|
type NetworkRequest struct {
|
||||||
|
// Description Network description
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
|
||||||
|
// Name Network name
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkResource defines model for NetworkResource.
|
||||||
|
type NetworkResource struct {
|
||||||
|
// Address Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or domains like example.com and *.example.com)
|
||||||
|
Address string `json:"address"`
|
||||||
|
|
||||||
|
// Description Network resource description
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
|
||||||
|
// Groups Groups that the resource belongs to
|
||||||
|
Groups []GroupMinimum `json:"groups"`
|
||||||
|
|
||||||
|
// Id Network Resource ID
|
||||||
|
Id string `json:"id"`
|
||||||
|
|
||||||
|
// Name Network resource name
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Type Network resource type based of the address
|
||||||
|
Type NetworkResourceType `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkResourceMinimum defines model for NetworkResourceMinimum.
|
||||||
|
type NetworkResourceMinimum struct {
|
||||||
|
// Address Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or domains like example.com and *.example.com)
|
||||||
|
Address string `json:"address"`
|
||||||
|
|
||||||
|
// Description Network resource description
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
|
||||||
|
// Name Network resource name
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkResourceRequest defines model for NetworkResourceRequest.
|
||||||
|
type NetworkResourceRequest struct {
|
||||||
|
// Address Network resource address (either a direct host like 1.1.1.1 or 1.1.1.1/32, or a subnet like 192.168.178.0/24, or domains like example.com and *.example.com)
|
||||||
|
Address string `json:"address"`
|
||||||
|
|
||||||
|
// Description Network resource description
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
|
||||||
|
// Groups Group IDs containing the resource
|
||||||
|
Groups []string `json:"groups"`
|
||||||
|
|
||||||
|
// Name Network resource name
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkResourceType Network resource type based of the address
|
||||||
|
type NetworkResourceType string
|
||||||
|
|
||||||
|
// NetworkRouter defines model for NetworkRouter.
|
||||||
|
type NetworkRouter struct {
|
||||||
|
// Id Network Router Id
|
||||||
|
Id string `json:"id"`
|
||||||
|
|
||||||
|
// Masquerade Indicate if peer should masquerade traffic to this route's prefix
|
||||||
|
Masquerade bool `json:"masquerade"`
|
||||||
|
|
||||||
|
// Metric Route metric number. Lowest number has higher priority
|
||||||
|
Metric int `json:"metric"`
|
||||||
|
|
||||||
|
// Peer Peer Identifier associated with route. This property can not be set together with `peer_groups`
|
||||||
|
Peer *string `json:"peer,omitempty"`
|
||||||
|
|
||||||
|
// PeerGroups Peers Group Identifier associated with route. This property can not be set together with `peer`
|
||||||
|
PeerGroups *[]string `json:"peer_groups,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkRouterRequest defines model for NetworkRouterRequest.
|
||||||
|
type NetworkRouterRequest struct {
|
||||||
|
// Masquerade Indicate if peer should masquerade traffic to this route's prefix
|
||||||
|
Masquerade bool `json:"masquerade"`
|
||||||
|
|
||||||
|
// Metric Route metric number. Lowest number has higher priority
|
||||||
|
Metric int `json:"metric"`
|
||||||
|
|
||||||
|
// Peer Peer Identifier associated with route. This property can not be set together with `peer_groups`
|
||||||
|
Peer *string `json:"peer,omitempty"`
|
||||||
|
|
||||||
|
// PeerGroups Peers Group Identifier associated with route. This property can not be set together with `peer`
|
||||||
|
PeerGroups *[]string `json:"peer_groups,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// OSVersionCheck Posture check for the version of operating system
|
// OSVersionCheck Posture check for the version of operating system
|
||||||
type OSVersionCheck struct {
|
type OSVersionCheck struct {
|
||||||
// Android Posture check for the version of operating system
|
// Android Posture check for the version of operating system
|
||||||
@ -779,10 +921,11 @@ type PolicyRule struct {
|
|||||||
Bidirectional bool `json:"bidirectional"`
|
Bidirectional bool `json:"bidirectional"`
|
||||||
|
|
||||||
// Description Policy rule friendly description
|
// Description Policy rule friendly description
|
||||||
Description *string `json:"description,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
|
DestinationResource *Resource `json:"destinationResource,omitempty"`
|
||||||
|
|
||||||
// Destinations Policy rule destination group IDs
|
// Destinations Policy rule destination group IDs
|
||||||
Destinations []GroupMinimum `json:"destinations"`
|
Destinations *[]GroupMinimum `json:"destinations,omitempty"`
|
||||||
|
|
||||||
// Enabled Policy rule status
|
// Enabled Policy rule status
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
@ -800,10 +943,11 @@ type PolicyRule struct {
|
|||||||
Ports *[]string `json:"ports,omitempty"`
|
Ports *[]string `json:"ports,omitempty"`
|
||||||
|
|
||||||
// Protocol Policy rule type of the traffic
|
// Protocol Policy rule type of the traffic
|
||||||
Protocol PolicyRuleProtocol `json:"protocol"`
|
Protocol PolicyRuleProtocol `json:"protocol"`
|
||||||
|
SourceResource *Resource `json:"sourceResource,omitempty"`
|
||||||
|
|
||||||
// Sources Policy rule source group IDs
|
// Sources Policy rule source group IDs
|
||||||
Sources []GroupMinimum `json:"sources"`
|
Sources *[]GroupMinimum `json:"sources,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PolicyRuleAction Policy rule accept or drops packets
|
// PolicyRuleAction Policy rule accept or drops packets
|
||||||
@ -857,10 +1001,11 @@ type PolicyRuleUpdate struct {
|
|||||||
Bidirectional bool `json:"bidirectional"`
|
Bidirectional bool `json:"bidirectional"`
|
||||||
|
|
||||||
// Description Policy rule friendly description
|
// Description Policy rule friendly description
|
||||||
Description *string `json:"description,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
|
DestinationResource *Resource `json:"destinationResource,omitempty"`
|
||||||
|
|
||||||
// Destinations Policy rule destination group IDs
|
// Destinations Policy rule destination group IDs
|
||||||
Destinations []string `json:"destinations"`
|
Destinations *[]string `json:"destinations,omitempty"`
|
||||||
|
|
||||||
// Enabled Policy rule status
|
// Enabled Policy rule status
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
@ -878,10 +1023,11 @@ type PolicyRuleUpdate struct {
|
|||||||
Ports *[]string `json:"ports,omitempty"`
|
Ports *[]string `json:"ports,omitempty"`
|
||||||
|
|
||||||
// Protocol Policy rule type of the traffic
|
// Protocol Policy rule type of the traffic
|
||||||
Protocol PolicyRuleUpdateProtocol `json:"protocol"`
|
Protocol PolicyRuleUpdateProtocol `json:"protocol"`
|
||||||
|
SourceResource *Resource `json:"sourceResource,omitempty"`
|
||||||
|
|
||||||
// Sources Policy rule source group IDs
|
// Sources Policy rule source group IDs
|
||||||
Sources []string `json:"sources"`
|
Sources *[]string `json:"sources,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PolicyRuleUpdateAction Policy rule accept or drops packets
|
// PolicyRuleUpdateAction Policy rule accept or drops packets
|
||||||
@ -955,6 +1101,16 @@ type ProcessCheck struct {
|
|||||||
Processes []Process `json:"processes"`
|
Processes []Process `json:"processes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resource defines model for Resource.
|
||||||
|
type Resource struct {
|
||||||
|
// Id ID of the resource
|
||||||
|
Id string `json:"id"`
|
||||||
|
Type ResourceType `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResourceType defines model for ResourceType.
|
||||||
|
type ResourceType string
|
||||||
|
|
||||||
// Route defines model for Route.
|
// Route defines model for Route.
|
||||||
type Route struct {
|
type Route struct {
|
||||||
// AccessControlGroups Access control group identifier associated with route.
|
// AccessControlGroups Access control group identifier associated with route.
|
||||||
@ -1292,6 +1448,24 @@ type PostApiGroupsJSONRequestBody = GroupRequest
|
|||||||
// PutApiGroupsGroupIdJSONRequestBody defines body for PutApiGroupsGroupId for application/json ContentType.
|
// PutApiGroupsGroupIdJSONRequestBody defines body for PutApiGroupsGroupId for application/json ContentType.
|
||||||
type PutApiGroupsGroupIdJSONRequestBody = GroupRequest
|
type PutApiGroupsGroupIdJSONRequestBody = GroupRequest
|
||||||
|
|
||||||
|
// PostApiNetworksJSONRequestBody defines body for PostApiNetworks for application/json ContentType.
|
||||||
|
type PostApiNetworksJSONRequestBody = NetworkRequest
|
||||||
|
|
||||||
|
// PutApiNetworksNetworkIdJSONRequestBody defines body for PutApiNetworksNetworkId for application/json ContentType.
|
||||||
|
type PutApiNetworksNetworkIdJSONRequestBody = NetworkRequest
|
||||||
|
|
||||||
|
// PostApiNetworksNetworkIdResourcesJSONRequestBody defines body for PostApiNetworksNetworkIdResources for application/json ContentType.
|
||||||
|
type PostApiNetworksNetworkIdResourcesJSONRequestBody = NetworkResourceRequest
|
||||||
|
|
||||||
|
// PutApiNetworksNetworkIdResourcesResourceIdJSONRequestBody defines body for PutApiNetworksNetworkIdResourcesResourceId for application/json ContentType.
|
||||||
|
type PutApiNetworksNetworkIdResourcesResourceIdJSONRequestBody = NetworkResourceRequest
|
||||||
|
|
||||||
|
// PostApiNetworksNetworkIdRoutersJSONRequestBody defines body for PostApiNetworksNetworkIdRouters for application/json ContentType.
|
||||||
|
type PostApiNetworksNetworkIdRoutersJSONRequestBody = NetworkRouterRequest
|
||||||
|
|
||||||
|
// PutApiNetworksNetworkIdRoutersRouterIdJSONRequestBody defines body for PutApiNetworksNetworkIdRoutersRouterId for application/json ContentType.
|
||||||
|
type PutApiNetworksNetworkIdRoutersRouterIdJSONRequestBody = NetworkRouterRequest
|
||||||
|
|
||||||
// PutApiPeersPeerIdJSONRequestBody defines body for PutApiPeersPeerId for application/json ContentType.
|
// PutApiPeersPeerIdJSONRequestBody defines body for PutApiPeersPeerId for application/json ContentType.
|
||||||
type PutApiPeersPeerIdJSONRequestBody = PeerRequest
|
type PutApiPeersPeerIdJSONRequestBody = PeerRequest
|
||||||
|
|
||||||
|
@ -12,11 +12,13 @@ import (
|
|||||||
|
|
||||||
s "github.com/netbirdio/netbird/management/server"
|
s "github.com/netbirdio/netbird/management/server"
|
||||||
"github.com/netbirdio/netbird/management/server/geolocation"
|
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||||
|
nbgroups "github.com/netbirdio/netbird/management/server/groups"
|
||||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||||
"github.com/netbirdio/netbird/management/server/http/handlers/accounts"
|
"github.com/netbirdio/netbird/management/server/http/handlers/accounts"
|
||||||
"github.com/netbirdio/netbird/management/server/http/handlers/dns"
|
"github.com/netbirdio/netbird/management/server/http/handlers/dns"
|
||||||
"github.com/netbirdio/netbird/management/server/http/handlers/events"
|
"github.com/netbirdio/netbird/management/server/http/handlers/events"
|
||||||
"github.com/netbirdio/netbird/management/server/http/handlers/groups"
|
"github.com/netbirdio/netbird/management/server/http/handlers/groups"
|
||||||
|
"github.com/netbirdio/netbird/management/server/http/handlers/networks"
|
||||||
"github.com/netbirdio/netbird/management/server/http/handlers/peers"
|
"github.com/netbirdio/netbird/management/server/http/handlers/peers"
|
||||||
"github.com/netbirdio/netbird/management/server/http/handlers/policies"
|
"github.com/netbirdio/netbird/management/server/http/handlers/policies"
|
||||||
"github.com/netbirdio/netbird/management/server/http/handlers/routes"
|
"github.com/netbirdio/netbird/management/server/http/handlers/routes"
|
||||||
@ -25,6 +27,9 @@ import (
|
|||||||
"github.com/netbirdio/netbird/management/server/http/middleware"
|
"github.com/netbirdio/netbird/management/server/http/middleware"
|
||||||
"github.com/netbirdio/netbird/management/server/integrated_validator"
|
"github.com/netbirdio/netbird/management/server/integrated_validator"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
|
nbnetworks "github.com/netbirdio/netbird/management/server/networks"
|
||||||
|
"github.com/netbirdio/netbird/management/server/networks/resources"
|
||||||
|
"github.com/netbirdio/netbird/management/server/networks/routers"
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -38,7 +43,7 @@ type apiHandler struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// APIHandler creates the Management service HTTP API handler registering all the available endpoints.
|
// APIHandler creates the Management service HTTP API handler registering all the available endpoints.
|
||||||
func APIHandler(ctx context.Context, accountManager s.AccountManager, LocationManager *geolocation.Geolocation, jwtValidator jwtclaims.JWTValidator, appMetrics telemetry.AppMetrics, authCfg configs.AuthCfg, integratedValidator integrated_validator.IntegratedValidator) (http.Handler, error) {
|
func APIHandler(ctx context.Context, accountManager s.AccountManager, networksManager nbnetworks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager nbgroups.Manager, LocationManager *geolocation.Geolocation, jwtValidator jwtclaims.JWTValidator, appMetrics telemetry.AppMetrics, authCfg configs.AuthCfg, integratedValidator integrated_validator.IntegratedValidator) (http.Handler, error) {
|
||||||
claimsExtractor := jwtclaims.NewClaimsExtractor(
|
claimsExtractor := jwtclaims.NewClaimsExtractor(
|
||||||
jwtclaims.WithAudience(authCfg.Audience),
|
jwtclaims.WithAudience(authCfg.Audience),
|
||||||
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
||||||
@ -93,6 +98,7 @@ func APIHandler(ctx context.Context, accountManager s.AccountManager, LocationMa
|
|||||||
routes.AddEndpoints(api.AccountManager, authCfg, router)
|
routes.AddEndpoints(api.AccountManager, authCfg, router)
|
||||||
dns.AddEndpoints(api.AccountManager, authCfg, router)
|
dns.AddEndpoints(api.AccountManager, authCfg, router)
|
||||||
events.AddEndpoints(api.AccountManager, authCfg, router)
|
events.AddEndpoints(api.AccountManager, authCfg, router)
|
||||||
|
networks.AddEndpoints(networksManager, resourceManager, routerManager, groupsManager, api.AccountManager, api.AccountManager.GetAccountIDFromToken, authCfg, router)
|
||||||
|
|
||||||
return rootRouter, nil
|
return rootRouter, nil
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/netbirdio/netbird/management/server/http/util"
|
"github.com/netbirdio/netbird/management/server/http/util"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// handler is a handler that handles the server.Account HTTP endpoints
|
// handler is a handler that handles the server.Account HTTP endpoints
|
||||||
@ -82,7 +83,7 @@ func (h *handler) updateAccount(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
settings := &server.Settings{
|
settings := &types.Settings{
|
||||||
PeerLoginExpirationEnabled: req.Settings.PeerLoginExpirationEnabled,
|
PeerLoginExpirationEnabled: req.Settings.PeerLoginExpirationEnabled,
|
||||||
PeerLoginExpiration: time.Duration(float64(time.Second.Nanoseconds()) * float64(req.Settings.PeerLoginExpiration)),
|
PeerLoginExpiration: time.Duration(float64(time.Second.Nanoseconds()) * float64(req.Settings.PeerLoginExpiration)),
|
||||||
RegularUsersViewBlocked: req.Settings.RegularUsersViewBlocked,
|
RegularUsersViewBlocked: req.Settings.RegularUsersViewBlocked,
|
||||||
@ -107,6 +108,9 @@ func (h *handler) updateAccount(w http.ResponseWriter, r *http.Request) {
|
|||||||
if req.Settings.JwtAllowGroups != nil {
|
if req.Settings.JwtAllowGroups != nil {
|
||||||
settings.JWTAllowGroups = *req.Settings.JwtAllowGroups
|
settings.JWTAllowGroups = *req.Settings.JwtAllowGroups
|
||||||
}
|
}
|
||||||
|
if req.Settings.RoutingPeerDnsResolutionEnabled != nil {
|
||||||
|
settings.RoutingPeerDNSResolutionEnabled = *req.Settings.RoutingPeerDnsResolutionEnabled
|
||||||
|
}
|
||||||
|
|
||||||
updatedAccount, err := h.accountManager.UpdateAccountSettings(r.Context(), accountID, userID, settings)
|
updatedAccount, err := h.accountManager.UpdateAccountSettings(r.Context(), accountID, userID, settings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -138,7 +142,7 @@ func (h *handler) deleteAccount(w http.ResponseWriter, r *http.Request) {
|
|||||||
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func toAccountResponse(accountID string, settings *server.Settings) *api.Account {
|
func toAccountResponse(accountID string, settings *types.Settings) *api.Account {
|
||||||
jwtAllowGroups := settings.JWTAllowGroups
|
jwtAllowGroups := settings.JWTAllowGroups
|
||||||
if jwtAllowGroups == nil {
|
if jwtAllowGroups == nil {
|
||||||
jwtAllowGroups = []string{}
|
jwtAllowGroups = []string{}
|
||||||
@ -154,6 +158,7 @@ func toAccountResponse(accountID string, settings *server.Settings) *api.Account
|
|||||||
JwtGroupsClaimName: &settings.JWTGroupsClaimName,
|
JwtGroupsClaimName: &settings.JWTGroupsClaimName,
|
||||||
JwtAllowGroups: &jwtAllowGroups,
|
JwtAllowGroups: &jwtAllowGroups,
|
||||||
RegularUsersViewBlocked: settings.RegularUsersViewBlocked,
|
RegularUsersViewBlocked: settings.RegularUsersViewBlocked,
|
||||||
|
RoutingPeerDnsResolutionEnabled: &settings.RoutingPeerDNSResolutionEnabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.Extra != nil {
|
if settings.Extra != nil {
|
||||||
|
@ -13,23 +13,23 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server"
|
|
||||||
"github.com/netbirdio/netbird/management/server/http/api"
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
"github.com/netbirdio/netbird/management/server/mock_server"
|
"github.com/netbirdio/netbird/management/server/mock_server"
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initAccountsTestData(account *server.Account, admin *server.User) *handler {
|
func initAccountsTestData(account *types.Account, admin *types.User) *handler {
|
||||||
return &handler{
|
return &handler{
|
||||||
accountManager: &mock_server.MockAccountManager{
|
accountManager: &mock_server.MockAccountManager{
|
||||||
GetAccountIDFromTokenFunc: func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
GetAccountIDFromTokenFunc: func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
||||||
return account.Id, admin.Id, nil
|
return account.Id, admin.Id, nil
|
||||||
},
|
},
|
||||||
GetAccountSettingsFunc: func(ctx context.Context, accountID string, userID string) (*server.Settings, error) {
|
GetAccountSettingsFunc: func(ctx context.Context, accountID string, userID string) (*types.Settings, error) {
|
||||||
return account.Settings, nil
|
return account.Settings, nil
|
||||||
},
|
},
|
||||||
UpdateAccountSettingsFunc: func(ctx context.Context, accountID, userID string, newSettings *server.Settings) (*server.Account, error) {
|
UpdateAccountSettingsFunc: func(ctx context.Context, accountID, userID string, newSettings *types.Settings) (*types.Account, error) {
|
||||||
halfYearLimit := 180 * 24 * time.Hour
|
halfYearLimit := 180 * 24 * time.Hour
|
||||||
if newSettings.PeerLoginExpiration > halfYearLimit {
|
if newSettings.PeerLoginExpiration > halfYearLimit {
|
||||||
return nil, status.Errorf(status.InvalidArgument, "peer login expiration can't be larger than 180 days")
|
return nil, status.Errorf(status.InvalidArgument, "peer login expiration can't be larger than 180 days")
|
||||||
@ -58,19 +58,19 @@ func initAccountsTestData(account *server.Account, admin *server.User) *handler
|
|||||||
|
|
||||||
func TestAccounts_AccountsHandler(t *testing.T) {
|
func TestAccounts_AccountsHandler(t *testing.T) {
|
||||||
accountID := "test_account"
|
accountID := "test_account"
|
||||||
adminUser := server.NewAdminUser("test_user")
|
adminUser := types.NewAdminUser("test_user")
|
||||||
|
|
||||||
sr := func(v string) *string { return &v }
|
sr := func(v string) *string { return &v }
|
||||||
br := func(v bool) *bool { return &v }
|
br := func(v bool) *bool { return &v }
|
||||||
|
|
||||||
handler := initAccountsTestData(&server.Account{
|
handler := initAccountsTestData(&types.Account{
|
||||||
Id: accountID,
|
Id: accountID,
|
||||||
Domain: "hotmail.com",
|
Domain: "hotmail.com",
|
||||||
Network: server.NewNetwork(),
|
Network: types.NewNetwork(),
|
||||||
Users: map[string]*server.User{
|
Users: map[string]*types.User{
|
||||||
adminUser.Id: adminUser,
|
adminUser.Id: adminUser,
|
||||||
},
|
},
|
||||||
Settings: &server.Settings{
|
Settings: &types.Settings{
|
||||||
PeerLoginExpirationEnabled: false,
|
PeerLoginExpirationEnabled: false,
|
||||||
PeerLoginExpiration: time.Hour,
|
PeerLoginExpiration: time.Hour,
|
||||||
RegularUsersViewBlocked: true,
|
RegularUsersViewBlocked: true,
|
||||||
@ -95,13 +95,14 @@ func TestAccounts_AccountsHandler(t *testing.T) {
|
|||||||
requestPath: "/api/accounts",
|
requestPath: "/api/accounts",
|
||||||
expectedStatus: http.StatusOK,
|
expectedStatus: http.StatusOK,
|
||||||
expectedSettings: api.AccountSettings{
|
expectedSettings: api.AccountSettings{
|
||||||
PeerLoginExpiration: int(time.Hour.Seconds()),
|
PeerLoginExpiration: int(time.Hour.Seconds()),
|
||||||
PeerLoginExpirationEnabled: false,
|
PeerLoginExpirationEnabled: false,
|
||||||
GroupsPropagationEnabled: br(false),
|
GroupsPropagationEnabled: br(false),
|
||||||
JwtGroupsClaimName: sr(""),
|
JwtGroupsClaimName: sr(""),
|
||||||
JwtGroupsEnabled: br(false),
|
JwtGroupsEnabled: br(false),
|
||||||
JwtAllowGroups: &[]string{},
|
JwtAllowGroups: &[]string{},
|
||||||
RegularUsersViewBlocked: true,
|
RegularUsersViewBlocked: true,
|
||||||
|
RoutingPeerDnsResolutionEnabled: br(false),
|
||||||
},
|
},
|
||||||
expectedArray: true,
|
expectedArray: true,
|
||||||
expectedID: accountID,
|
expectedID: accountID,
|
||||||
@ -114,13 +115,14 @@ func TestAccounts_AccountsHandler(t *testing.T) {
|
|||||||
requestBody: bytes.NewBufferString("{\"settings\": {\"peer_login_expiration\": 15552000,\"peer_login_expiration_enabled\": true}}"),
|
requestBody: bytes.NewBufferString("{\"settings\": {\"peer_login_expiration\": 15552000,\"peer_login_expiration_enabled\": true}}"),
|
||||||
expectedStatus: http.StatusOK,
|
expectedStatus: http.StatusOK,
|
||||||
expectedSettings: api.AccountSettings{
|
expectedSettings: api.AccountSettings{
|
||||||
PeerLoginExpiration: 15552000,
|
PeerLoginExpiration: 15552000,
|
||||||
PeerLoginExpirationEnabled: true,
|
PeerLoginExpirationEnabled: true,
|
||||||
GroupsPropagationEnabled: br(false),
|
GroupsPropagationEnabled: br(false),
|
||||||
JwtGroupsClaimName: sr(""),
|
JwtGroupsClaimName: sr(""),
|
||||||
JwtGroupsEnabled: br(false),
|
JwtGroupsEnabled: br(false),
|
||||||
JwtAllowGroups: &[]string{},
|
JwtAllowGroups: &[]string{},
|
||||||
RegularUsersViewBlocked: false,
|
RegularUsersViewBlocked: false,
|
||||||
|
RoutingPeerDnsResolutionEnabled: br(false),
|
||||||
},
|
},
|
||||||
expectedArray: false,
|
expectedArray: false,
|
||||||
expectedID: accountID,
|
expectedID: accountID,
|
||||||
@ -133,13 +135,14 @@ func TestAccounts_AccountsHandler(t *testing.T) {
|
|||||||
requestBody: bytes.NewBufferString("{\"settings\": {\"peer_login_expiration\": 15552000,\"peer_login_expiration_enabled\": false,\"jwt_groups_enabled\":true,\"jwt_groups_claim_name\":\"roles\",\"jwt_allow_groups\":[\"test\"],\"regular_users_view_blocked\":true}}"),
|
requestBody: bytes.NewBufferString("{\"settings\": {\"peer_login_expiration\": 15552000,\"peer_login_expiration_enabled\": false,\"jwt_groups_enabled\":true,\"jwt_groups_claim_name\":\"roles\",\"jwt_allow_groups\":[\"test\"],\"regular_users_view_blocked\":true}}"),
|
||||||
expectedStatus: http.StatusOK,
|
expectedStatus: http.StatusOK,
|
||||||
expectedSettings: api.AccountSettings{
|
expectedSettings: api.AccountSettings{
|
||||||
PeerLoginExpiration: 15552000,
|
PeerLoginExpiration: 15552000,
|
||||||
PeerLoginExpirationEnabled: false,
|
PeerLoginExpirationEnabled: false,
|
||||||
GroupsPropagationEnabled: br(false),
|
GroupsPropagationEnabled: br(false),
|
||||||
JwtGroupsClaimName: sr("roles"),
|
JwtGroupsClaimName: sr("roles"),
|
||||||
JwtGroupsEnabled: br(true),
|
JwtGroupsEnabled: br(true),
|
||||||
JwtAllowGroups: &[]string{"test"},
|
JwtAllowGroups: &[]string{"test"},
|
||||||
RegularUsersViewBlocked: true,
|
RegularUsersViewBlocked: true,
|
||||||
|
RoutingPeerDnsResolutionEnabled: br(false),
|
||||||
},
|
},
|
||||||
expectedArray: false,
|
expectedArray: false,
|
||||||
expectedID: accountID,
|
expectedID: accountID,
|
||||||
@ -152,13 +155,14 @@ func TestAccounts_AccountsHandler(t *testing.T) {
|
|||||||
requestBody: bytes.NewBufferString("{\"settings\": {\"peer_login_expiration\": 554400,\"peer_login_expiration_enabled\": true,\"jwt_groups_enabled\":true,\"jwt_groups_claim_name\":\"groups\",\"groups_propagation_enabled\":true,\"regular_users_view_blocked\":true}}"),
|
requestBody: bytes.NewBufferString("{\"settings\": {\"peer_login_expiration\": 554400,\"peer_login_expiration_enabled\": true,\"jwt_groups_enabled\":true,\"jwt_groups_claim_name\":\"groups\",\"groups_propagation_enabled\":true,\"regular_users_view_blocked\":true}}"),
|
||||||
expectedStatus: http.StatusOK,
|
expectedStatus: http.StatusOK,
|
||||||
expectedSettings: api.AccountSettings{
|
expectedSettings: api.AccountSettings{
|
||||||
PeerLoginExpiration: 554400,
|
PeerLoginExpiration: 554400,
|
||||||
PeerLoginExpirationEnabled: true,
|
PeerLoginExpirationEnabled: true,
|
||||||
GroupsPropagationEnabled: br(true),
|
GroupsPropagationEnabled: br(true),
|
||||||
JwtGroupsClaimName: sr("groups"),
|
JwtGroupsClaimName: sr("groups"),
|
||||||
JwtGroupsEnabled: br(true),
|
JwtGroupsEnabled: br(true),
|
||||||
JwtAllowGroups: &[]string{},
|
JwtAllowGroups: &[]string{},
|
||||||
RegularUsersViewBlocked: true,
|
RegularUsersViewBlocked: true,
|
||||||
|
RoutingPeerDnsResolutionEnabled: br(false),
|
||||||
},
|
},
|
||||||
expectedArray: false,
|
expectedArray: false,
|
||||||
expectedID: accountID,
|
expectedID: accountID,
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||||
"github.com/netbirdio/netbird/management/server/http/util"
|
"github.com/netbirdio/netbird/management/server/http/util"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// dnsSettingsHandler is a handler that returns the DNS settings of the account
|
// dnsSettingsHandler is a handler that returns the DNS settings of the account
|
||||||
@ -81,7 +82,7 @@ func (h *dnsSettingsHandler) updateDNSSettings(w http.ResponseWriter, r *http.Re
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDNSSettings := &server.DNSSettings{
|
updateDNSSettings := &types.DNSSettings{
|
||||||
DisabledManagementGroups: req.DisabledManagementGroups,
|
DisabledManagementGroups: req.DisabledManagementGroups,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,10 +13,10 @@ import (
|
|||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/http/api"
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server"
|
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
"github.com/netbirdio/netbird/management/server/mock_server"
|
"github.com/netbirdio/netbird/management/server/mock_server"
|
||||||
)
|
)
|
||||||
@ -27,15 +27,15 @@ const (
|
|||||||
testDNSSettingsUserID = "test_user"
|
testDNSSettingsUserID = "test_user"
|
||||||
)
|
)
|
||||||
|
|
||||||
var baseExistingDNSSettings = server.DNSSettings{
|
var baseExistingDNSSettings = types.DNSSettings{
|
||||||
DisabledManagementGroups: []string{testDNSSettingsExistingGroup},
|
DisabledManagementGroups: []string{testDNSSettingsExistingGroup},
|
||||||
}
|
}
|
||||||
|
|
||||||
var testingDNSSettingsAccount = &server.Account{
|
var testingDNSSettingsAccount = &types.Account{
|
||||||
Id: testDNSSettingsAccountID,
|
Id: testDNSSettingsAccountID,
|
||||||
Domain: "hotmail.com",
|
Domain: "hotmail.com",
|
||||||
Users: map[string]*server.User{
|
Users: map[string]*types.User{
|
||||||
testDNSSettingsUserID: server.NewAdminUser("test_user"),
|
testDNSSettingsUserID: types.NewAdminUser("test_user"),
|
||||||
},
|
},
|
||||||
DNSSettings: baseExistingDNSSettings,
|
DNSSettings: baseExistingDNSSettings,
|
||||||
}
|
}
|
||||||
@ -43,10 +43,10 @@ var testingDNSSettingsAccount = &server.Account{
|
|||||||
func initDNSSettingsTestData() *dnsSettingsHandler {
|
func initDNSSettingsTestData() *dnsSettingsHandler {
|
||||||
return &dnsSettingsHandler{
|
return &dnsSettingsHandler{
|
||||||
accountManager: &mock_server.MockAccountManager{
|
accountManager: &mock_server.MockAccountManager{
|
||||||
GetDNSSettingsFunc: func(ctx context.Context, accountID string, userID string) (*server.DNSSettings, error) {
|
GetDNSSettingsFunc: func(ctx context.Context, accountID string, userID string) (*types.DNSSettings, error) {
|
||||||
return &testingDNSSettingsAccount.DNSSettings, nil
|
return &testingDNSSettingsAccount.DNSSettings, nil
|
||||||
},
|
},
|
||||||
SaveDNSSettingsFunc: func(ctx context.Context, accountID string, userID string, dnsSettingsToSave *server.DNSSettings) error {
|
SaveDNSSettingsFunc: func(ctx context.Context, accountID string, userID string, dnsSettingsToSave *types.DNSSettings) error {
|
||||||
if dnsSettingsToSave != nil {
|
if dnsSettingsToSave != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,11 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server"
|
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
"github.com/netbirdio/netbird/management/server/http/api"
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
"github.com/netbirdio/netbird/management/server/mock_server"
|
"github.com/netbirdio/netbird/management/server/mock_server"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initEventsTestData(account string, events ...*activity.Event) *handler {
|
func initEventsTestData(account string, events ...*activity.Event) *handler {
|
||||||
@ -32,8 +32,8 @@ func initEventsTestData(account string, events ...*activity.Event) *handler {
|
|||||||
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
||||||
return claims.AccountId, claims.UserId, nil
|
return claims.AccountId, claims.UserId, nil
|
||||||
},
|
},
|
||||||
GetUsersFromAccountFunc: func(_ context.Context, accountID, userID string) ([]*server.UserInfo, error) {
|
GetUsersFromAccountFunc: func(_ context.Context, accountID, userID string) ([]*types.UserInfo, error) {
|
||||||
return make([]*server.UserInfo, 0), nil
|
return make([]*types.UserInfo, 0), nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||||
@ -191,7 +191,7 @@ func TestEvents_GetEvents(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
accountID := "test_account"
|
accountID := "test_account"
|
||||||
adminUser := server.NewAdminUser("test_user")
|
adminUser := types.NewAdminUser("test_user")
|
||||||
events := generateEvents(accountID, adminUser.Id)
|
events := generateEvents(accountID, adminUser.Id)
|
||||||
handler := initEventsTestData(accountID, events...)
|
handler := initEventsTestData(accountID, events...)
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ import (
|
|||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server"
|
"github.com/netbirdio/netbird/management/server"
|
||||||
nbgroup "github.com/netbirdio/netbird/management/server/group"
|
|
||||||
"github.com/netbirdio/netbird/management/server/http/api"
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
"github.com/netbirdio/netbird/management/server/http/util"
|
"github.com/netbirdio/netbird/management/server/http/util"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
@ -129,10 +129,21 @@ func (h *handler) updateGroup(w http.ResponseWriter, r *http.Request) {
|
|||||||
} else {
|
} else {
|
||||||
peers = *req.Peers
|
peers = *req.Peers
|
||||||
}
|
}
|
||||||
group := nbgroup.Group{
|
|
||||||
|
resources := make([]types.Resource, 0)
|
||||||
|
if req.Resources != nil {
|
||||||
|
for _, res := range *req.Resources {
|
||||||
|
resource := types.Resource{}
|
||||||
|
resource.FromAPIRequest(&res)
|
||||||
|
resources = append(resources, resource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group := types.Group{
|
||||||
ID: groupID,
|
ID: groupID,
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Peers: peers,
|
Peers: peers,
|
||||||
|
Resources: resources,
|
||||||
Issued: existingGroup.Issued,
|
Issued: existingGroup.Issued,
|
||||||
IntegrationReference: existingGroup.IntegrationReference,
|
IntegrationReference: existingGroup.IntegrationReference,
|
||||||
}
|
}
|
||||||
@ -179,10 +190,21 @@ func (h *handler) createGroup(w http.ResponseWriter, r *http.Request) {
|
|||||||
} else {
|
} else {
|
||||||
peers = *req.Peers
|
peers = *req.Peers
|
||||||
}
|
}
|
||||||
group := nbgroup.Group{
|
|
||||||
Name: req.Name,
|
resources := make([]types.Resource, 0)
|
||||||
Peers: peers,
|
if req.Resources != nil {
|
||||||
Issued: nbgroup.GroupIssuedAPI,
|
for _, res := range *req.Resources {
|
||||||
|
resource := types.Resource{}
|
||||||
|
resource.FromAPIRequest(&res)
|
||||||
|
resources = append(resources, resource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group := types.Group{
|
||||||
|
Name: req.Name,
|
||||||
|
Peers: peers,
|
||||||
|
Resources: resources,
|
||||||
|
Issued: types.GroupIssuedAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.accountManager.SaveGroup(r.Context(), accountID, userID, &group)
|
err = h.accountManager.SaveGroup(r.Context(), accountID, userID, &group)
|
||||||
@ -259,13 +281,13 @@ func (h *handler) getGroup(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func toGroupResponse(peers []*nbpeer.Peer, group *nbgroup.Group) *api.Group {
|
func toGroupResponse(peers []*nbpeer.Peer, group *types.Group) *api.Group {
|
||||||
peersMap := make(map[string]*nbpeer.Peer, len(peers))
|
peersMap := make(map[string]*nbpeer.Peer, len(peers))
|
||||||
for _, peer := range peers {
|
for _, peer := range peers {
|
||||||
peersMap[peer.ID] = peer
|
peersMap[peer.ID] = peer
|
||||||
}
|
}
|
||||||
|
|
||||||
cache := make(map[string]api.PeerMinimum)
|
peerCache := make(map[string]api.PeerMinimum)
|
||||||
gr := api.Group{
|
gr := api.Group{
|
||||||
Id: group.ID,
|
Id: group.ID,
|
||||||
Name: group.Name,
|
Name: group.Name,
|
||||||
@ -273,7 +295,7 @@ func toGroupResponse(peers []*nbpeer.Peer, group *nbgroup.Group) *api.Group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, pid := range group.Peers {
|
for _, pid := range group.Peers {
|
||||||
_, ok := cache[pid]
|
_, ok := peerCache[pid]
|
||||||
if !ok {
|
if !ok {
|
||||||
peer, ok := peersMap[pid]
|
peer, ok := peersMap[pid]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -283,12 +305,19 @@ func toGroupResponse(peers []*nbpeer.Peer, group *nbgroup.Group) *api.Group {
|
|||||||
Id: peer.ID,
|
Id: peer.ID,
|
||||||
Name: peer.Name,
|
Name: peer.Name,
|
||||||
}
|
}
|
||||||
cache[pid] = peerResp
|
peerCache[pid] = peerResp
|
||||||
gr.Peers = append(gr.Peers, peerResp)
|
gr.Peers = append(gr.Peers, peerResp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gr.PeersCount = len(gr.Peers)
|
gr.PeersCount = len(gr.Peers)
|
||||||
|
|
||||||
|
for _, res := range group.Resources {
|
||||||
|
resResp := res.ToAPIResponse()
|
||||||
|
gr.Resources = append(gr.Resources, *resResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
gr.ResourcesCount = len(gr.Resources)
|
||||||
|
|
||||||
return &gr
|
return &gr
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,13 @@ import (
|
|||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server"
|
"github.com/netbirdio/netbird/management/server"
|
||||||
nbgroup "github.com/netbirdio/netbird/management/server/group"
|
|
||||||
"github.com/netbirdio/netbird/management/server/http/api"
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
"github.com/netbirdio/netbird/management/server/http/util"
|
"github.com/netbirdio/netbird/management/server/http/util"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
"github.com/netbirdio/netbird/management/server/mock_server"
|
"github.com/netbirdio/netbird/management/server/mock_server"
|
||||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var TestPeers = map[string]*nbpeer.Peer{
|
var TestPeers = map[string]*nbpeer.Peer{
|
||||||
@ -31,20 +31,20 @@ var TestPeers = map[string]*nbpeer.Peer{
|
|||||||
"B": {Key: "B", ID: "peer-B-ID", IP: net.ParseIP("200.200.200.200")},
|
"B": {Key: "B", ID: "peer-B-ID", IP: net.ParseIP("200.200.200.200")},
|
||||||
}
|
}
|
||||||
|
|
||||||
func initGroupTestData(initGroups ...*nbgroup.Group) *handler {
|
func initGroupTestData(initGroups ...*types.Group) *handler {
|
||||||
return &handler{
|
return &handler{
|
||||||
accountManager: &mock_server.MockAccountManager{
|
accountManager: &mock_server.MockAccountManager{
|
||||||
SaveGroupFunc: func(_ context.Context, accountID, userID string, group *nbgroup.Group) error {
|
SaveGroupFunc: func(_ context.Context, accountID, userID string, group *types.Group) error {
|
||||||
if !strings.HasPrefix(group.ID, "id-") {
|
if !strings.HasPrefix(group.ID, "id-") {
|
||||||
group.ID = "id-was-set"
|
group.ID = "id-was-set"
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
GetGroupFunc: func(_ context.Context, _, groupID, _ string) (*nbgroup.Group, error) {
|
GetGroupFunc: func(_ context.Context, _, groupID, _ string) (*types.Group, error) {
|
||||||
groups := map[string]*nbgroup.Group{
|
groups := map[string]*types.Group{
|
||||||
"id-jwt-group": {ID: "id-jwt-group", Name: "From JWT", Issued: nbgroup.GroupIssuedJWT},
|
"id-jwt-group": {ID: "id-jwt-group", Name: "From JWT", Issued: types.GroupIssuedJWT},
|
||||||
"id-existed": {ID: "id-existed", Peers: []string{"A", "B"}, Issued: nbgroup.GroupIssuedAPI},
|
"id-existed": {ID: "id-existed", Peers: []string{"A", "B"}, Issued: types.GroupIssuedAPI},
|
||||||
"id-all": {ID: "id-all", Name: "All", Issued: nbgroup.GroupIssuedAPI},
|
"id-all": {ID: "id-all", Name: "All", Issued: types.GroupIssuedAPI},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, group := range initGroups {
|
for _, group := range initGroups {
|
||||||
@ -61,9 +61,9 @@ func initGroupTestData(initGroups ...*nbgroup.Group) *handler {
|
|||||||
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
||||||
return claims.AccountId, claims.UserId, nil
|
return claims.AccountId, claims.UserId, nil
|
||||||
},
|
},
|
||||||
GetGroupByNameFunc: func(ctx context.Context, groupName, _ string) (*nbgroup.Group, error) {
|
GetGroupByNameFunc: func(ctx context.Context, groupName, _ string) (*types.Group, error) {
|
||||||
if groupName == "All" {
|
if groupName == "All" {
|
||||||
return &nbgroup.Group{ID: "id-all", Name: "All", Issued: nbgroup.GroupIssuedAPI}, nil
|
return &types.Group{ID: "id-all", Name: "All", Issued: types.GroupIssuedAPI}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("unknown group name")
|
return nil, fmt.Errorf("unknown group name")
|
||||||
@ -120,7 +120,7 @@ func TestGetGroup(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
group := &nbgroup.Group{
|
group := &types.Group{
|
||||||
ID: "idofthegroup",
|
ID: "idofthegroup",
|
||||||
Name: "Group",
|
Name: "Group",
|
||||||
}
|
}
|
||||||
@ -154,7 +154,7 @@ func TestGetGroup(t *testing.T) {
|
|||||||
t.Fatalf("I don't know what I expected; %v", err)
|
t.Fatalf("I don't know what I expected; %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
got := &nbgroup.Group{}
|
got := &types.Group{}
|
||||||
if err = json.Unmarshal(content, &got); err != nil {
|
if err = json.Unmarshal(content, &got); err != nil {
|
||||||
t.Fatalf("Sent content is not in correct json format; %v", err)
|
t.Fatalf("Sent content is not in correct json format; %v", err)
|
||||||
}
|
}
|
||||||
|
321
management/server/http/handlers/networks/handler.go
Normal file
321
management/server/http/handlers/networks/handler.go
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
package networks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
|
s "github.com/netbirdio/netbird/management/server"
|
||||||
|
"github.com/netbirdio/netbird/management/server/groups"
|
||||||
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
|
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||||
|
"github.com/netbirdio/netbird/management/server/http/util"
|
||||||
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
|
"github.com/netbirdio/netbird/management/server/networks"
|
||||||
|
"github.com/netbirdio/netbird/management/server/networks/resources"
|
||||||
|
"github.com/netbirdio/netbird/management/server/networks/routers"
|
||||||
|
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||||
|
"github.com/netbirdio/netbird/management/server/networks/types"
|
||||||
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
nbtypes "github.com/netbirdio/netbird/management/server/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// handler is a handler that returns networks of the account
|
||||||
|
type handler struct {
|
||||||
|
networksManager networks.Manager
|
||||||
|
resourceManager resources.Manager
|
||||||
|
routerManager routers.Manager
|
||||||
|
accountManager s.AccountManager
|
||||||
|
|
||||||
|
groupsManager groups.Manager
|
||||||
|
extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error)
|
||||||
|
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddEndpoints(networksManager networks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager groups.Manager, accountManager s.AccountManager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg, router *mux.Router) {
|
||||||
|
addRouterEndpoints(routerManager, extractFromToken, authCfg, router)
|
||||||
|
addResourceEndpoints(resourceManager, groupsManager, extractFromToken, authCfg, router)
|
||||||
|
|
||||||
|
networksHandler := newHandler(networksManager, resourceManager, routerManager, groupsManager, accountManager, extractFromToken, authCfg)
|
||||||
|
router.HandleFunc("/networks", networksHandler.getAllNetworks).Methods("GET", "OPTIONS")
|
||||||
|
router.HandleFunc("/networks", networksHandler.createNetwork).Methods("POST", "OPTIONS")
|
||||||
|
router.HandleFunc("/networks/{networkId}", networksHandler.getNetwork).Methods("GET", "OPTIONS")
|
||||||
|
router.HandleFunc("/networks/{networkId}", networksHandler.updateNetwork).Methods("PUT", "OPTIONS")
|
||||||
|
router.HandleFunc("/networks/{networkId}", networksHandler.deleteNetwork).Methods("DELETE", "OPTIONS")
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHandler(networksManager networks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager groups.Manager, accountManager s.AccountManager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg) *handler {
|
||||||
|
return &handler{
|
||||||
|
networksManager: networksManager,
|
||||||
|
resourceManager: resourceManager,
|
||||||
|
routerManager: routerManager,
|
||||||
|
groupsManager: groupsManager,
|
||||||
|
accountManager: accountManager,
|
||||||
|
extractFromToken: extractFromToken,
|
||||||
|
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||||
|
jwtclaims.WithAudience(authCfg.Audience),
|
||||||
|
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) getAllNetworks(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
networks, err := h.networksManager.GetAllNetworks(r.Context(), accountID, userID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceIDs, err := h.resourceManager.GetAllResourceIDsInAccount(r.Context(), accountID, userID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groups, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
routers, err := h.routerManager.GetAllRoutersInAccount(r.Context(), accountID, userID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
account, err := h.accountManager.GetAccount(r.Context(), accountID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(r.Context(), w, h.generateNetworkResponse(networks, routers, resourceIDs, groups, account))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) createNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req api.NetworkRequest
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
network := &types.Network{}
|
||||||
|
network.FromAPIRequest(&req)
|
||||||
|
|
||||||
|
network.AccountID = accountID
|
||||||
|
network, err = h.networksManager.CreateNetwork(r.Context(), userID, network)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
account, err := h.accountManager.GetAccount(r.Context(), accountID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
policyIDs := account.GetPoliciesAppliedInNetwork(network.ID)
|
||||||
|
|
||||||
|
util.WriteJSONObject(r.Context(), w, network.ToAPIResponse([]string{}, []string{}, 0, policyIDs))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) getNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
networkID := vars["networkId"]
|
||||||
|
if len(networkID) == 0 {
|
||||||
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "invalid network ID"), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
network, err := h.networksManager.GetNetwork(r.Context(), accountID, userID, networkID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
routerIDs, resourceIDs, peerCount, err := h.collectIDsInNetwork(r.Context(), accountID, userID, networkID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
account, err := h.accountManager.GetAccount(r.Context(), accountID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
policyIDs := account.GetPoliciesAppliedInNetwork(networkID)
|
||||||
|
|
||||||
|
util.WriteJSONObject(r.Context(), w, network.ToAPIResponse(routerIDs, resourceIDs, peerCount, policyIDs))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) updateNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
networkID := vars["networkId"]
|
||||||
|
if len(networkID) == 0 {
|
||||||
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "invalid network ID"), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req api.NetworkRequest
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
network := &types.Network{}
|
||||||
|
network.FromAPIRequest(&req)
|
||||||
|
|
||||||
|
network.ID = networkID
|
||||||
|
network.AccountID = accountID
|
||||||
|
network, err = h.networksManager.UpdateNetwork(r.Context(), userID, network)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
routerIDs, resourceIDs, peerCount, err := h.collectIDsInNetwork(r.Context(), accountID, userID, networkID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
account, err := h.accountManager.GetAccount(r.Context(), accountID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
policyIDs := account.GetPoliciesAppliedInNetwork(networkID)
|
||||||
|
|
||||||
|
util.WriteJSONObject(r.Context(), w, network.ToAPIResponse(routerIDs, resourceIDs, peerCount, policyIDs))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) deleteNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
networkID := vars["networkId"]
|
||||||
|
if len(networkID) == 0 {
|
||||||
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "invalid network ID"), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.networksManager.DeleteNetwork(r.Context(), accountID, userID, networkID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) collectIDsInNetwork(ctx context.Context, accountID, userID, networkID string) ([]string, []string, int, error) {
|
||||||
|
resources, err := h.resourceManager.GetAllResourcesInNetwork(ctx, accountID, userID, networkID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, fmt.Errorf("failed to get resources in network: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resourceIDs []string
|
||||||
|
for _, resource := range resources {
|
||||||
|
resourceIDs = append(resourceIDs, resource.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
routers, err := h.routerManager.GetAllRoutersInNetwork(ctx, accountID, userID, networkID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, fmt.Errorf("failed to get routers in network: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
groups, err := h.groupsManager.GetAllGroups(ctx, accountID, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, fmt.Errorf("failed to get groups: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
peerCounter := 0
|
||||||
|
var routerIDs []string
|
||||||
|
for _, router := range routers {
|
||||||
|
routerIDs = append(routerIDs, router.ID)
|
||||||
|
if router.Peer != "" {
|
||||||
|
peerCounter++
|
||||||
|
}
|
||||||
|
if len(router.PeerGroups) > 0 {
|
||||||
|
for _, groupID := range router.PeerGroups {
|
||||||
|
peerCounter += len(groups[groupID].Peers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return routerIDs, resourceIDs, peerCounter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) generateNetworkResponse(networks []*types.Network, routers map[string][]*routerTypes.NetworkRouter, resourceIDs map[string][]string, groups map[string]*nbtypes.Group, account *nbtypes.Account) []*api.Network {
|
||||||
|
var networkResponse []*api.Network
|
||||||
|
for _, network := range networks {
|
||||||
|
routerIDs, peerCounter := getRouterIDs(network, routers, groups)
|
||||||
|
policyIDs := account.GetPoliciesAppliedInNetwork(network.ID)
|
||||||
|
networkResponse = append(networkResponse, network.ToAPIResponse(routerIDs, resourceIDs[network.ID], peerCounter, policyIDs))
|
||||||
|
}
|
||||||
|
return networkResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRouterIDs(network *types.Network, routers map[string][]*routerTypes.NetworkRouter, groups map[string]*nbtypes.Group) ([]string, int) {
|
||||||
|
routerIDs := []string{}
|
||||||
|
peerCounter := 0
|
||||||
|
for _, router := range routers[network.ID] {
|
||||||
|
routerIDs = append(routerIDs, router.ID)
|
||||||
|
if router.Peer != "" {
|
||||||
|
peerCounter++
|
||||||
|
}
|
||||||
|
if len(router.PeerGroups) > 0 {
|
||||||
|
for _, groupID := range router.PeerGroups {
|
||||||
|
group, ok := groups[groupID]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
peerCounter += len(group.Peers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return routerIDs, peerCounter
|
||||||
|
}
|
222
management/server/http/handlers/networks/resources_handler.go
Normal file
222
management/server/http/handlers/networks/resources_handler.go
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
package networks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/server/groups"
|
||||||
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
|
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||||
|
"github.com/netbirdio/netbird/management/server/http/util"
|
||||||
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
|
"github.com/netbirdio/netbird/management/server/networks/resources"
|
||||||
|
"github.com/netbirdio/netbird/management/server/networks/resources/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type resourceHandler struct {
|
||||||
|
resourceManager resources.Manager
|
||||||
|
groupsManager groups.Manager
|
||||||
|
extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error)
|
||||||
|
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||||
|
}
|
||||||
|
|
||||||
|
func addResourceEndpoints(resourcesManager resources.Manager, groupsManager groups.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg, router *mux.Router) {
|
||||||
|
resourceHandler := newResourceHandler(resourcesManager, groupsManager, extractFromToken, authCfg)
|
||||||
|
router.HandleFunc("/networks/resources", resourceHandler.getAllResourcesInAccount).Methods("GET", "OPTIONS")
|
||||||
|
router.HandleFunc("/networks/{networkId}/resources", resourceHandler.getAllResourcesInNetwork).Methods("GET", "OPTIONS")
|
||||||
|
router.HandleFunc("/networks/{networkId}/resources", resourceHandler.createResource).Methods("POST", "OPTIONS")
|
||||||
|
router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.getResource).Methods("GET", "OPTIONS")
|
||||||
|
router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.updateResource).Methods("PUT", "OPTIONS")
|
||||||
|
router.HandleFunc("/networks/{networkId}/resources/{resourceId}", resourceHandler.deleteResource).Methods("DELETE", "OPTIONS")
|
||||||
|
}
|
||||||
|
|
||||||
|
func newResourceHandler(resourceManager resources.Manager, groupsManager groups.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg) *resourceHandler {
|
||||||
|
return &resourceHandler{
|
||||||
|
resourceManager: resourceManager,
|
||||||
|
groupsManager: groupsManager,
|
||||||
|
extractFromToken: extractFromToken,
|
||||||
|
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||||
|
jwtclaims.WithAudience(authCfg.Audience),
|
||||||
|
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *resourceHandler) getAllResourcesInNetwork(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
networkID := mux.Vars(r)["networkId"]
|
||||||
|
resources, err := h.resourceManager.GetAllResourcesInNetwork(r.Context(), accountID, userID, networkID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var resourcesResponse []*api.NetworkResource
|
||||||
|
for _, resource := range resources {
|
||||||
|
groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
|
||||||
|
resourcesResponse = append(resourcesResponse, resource.ToAPIResponse(groupMinimumInfo))
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(r.Context(), w, resourcesResponse)
|
||||||
|
}
|
||||||
|
func (h *resourceHandler) getAllResourcesInAccount(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resources, err := h.resourceManager.GetAllResourcesInAccount(r.Context(), accountID, userID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var resourcesResponse []*api.NetworkResource
|
||||||
|
for _, resource := range resources {
|
||||||
|
groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
|
||||||
|
resourcesResponse = append(resourcesResponse, resource.ToAPIResponse(groupMinimumInfo))
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(r.Context(), w, resourcesResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *resourceHandler) createResource(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req api.NetworkResourceRequest
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resource := &types.NetworkResource{}
|
||||||
|
resource.FromAPIRequest(&req)
|
||||||
|
|
||||||
|
resource.NetworkID = mux.Vars(r)["networkId"]
|
||||||
|
resource.AccountID = accountID
|
||||||
|
resource, err = h.resourceManager.CreateResource(r.Context(), userID, resource)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
|
||||||
|
util.WriteJSONObject(r.Context(), w, resource.ToAPIResponse(groupMinimumInfo))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *resourceHandler) getResource(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
networkID := mux.Vars(r)["networkId"]
|
||||||
|
resourceID := mux.Vars(r)["resourceId"]
|
||||||
|
resource, err := h.resourceManager.GetResource(r.Context(), accountID, userID, networkID, resourceID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
|
||||||
|
util.WriteJSONObject(r.Context(), w, resource.ToAPIResponse(groupMinimumInfo))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *resourceHandler) updateResource(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req api.NetworkResourceRequest
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resource := &types.NetworkResource{}
|
||||||
|
resource.FromAPIRequest(&req)
|
||||||
|
|
||||||
|
resource.ID = mux.Vars(r)["resourceId"]
|
||||||
|
resource.NetworkID = mux.Vars(r)["networkId"]
|
||||||
|
resource.AccountID = accountID
|
||||||
|
resource, err = h.resourceManager.UpdateResource(r.Context(), userID, resource)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
grps, err := h.groupsManager.GetAllGroups(r.Context(), accountID, userID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groupMinimumInfo := groups.ToGroupsInfo(grps, resource.ID)
|
||||||
|
util.WriteJSONObject(r.Context(), w, resource.ToAPIResponse(groupMinimumInfo))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *resourceHandler) deleteResource(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
networkID := mux.Vars(r)["networkId"]
|
||||||
|
resourceID := mux.Vars(r)["resourceId"]
|
||||||
|
err = h.resourceManager.DeleteResource(r.Context(), accountID, userID, networkID, resourceID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
||||||
|
}
|
165
management/server/http/handlers/networks/routers_handler.go
Normal file
165
management/server/http/handlers/networks/routers_handler.go
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
package networks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
|
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||||
|
"github.com/netbirdio/netbird/management/server/http/util"
|
||||||
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
|
"github.com/netbirdio/netbird/management/server/networks/routers"
|
||||||
|
"github.com/netbirdio/netbird/management/server/networks/routers/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type routersHandler struct {
|
||||||
|
routersManager routers.Manager
|
||||||
|
extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error)
|
||||||
|
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||||
|
}
|
||||||
|
|
||||||
|
func addRouterEndpoints(routersManager routers.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg, router *mux.Router) {
|
||||||
|
routersHandler := newRoutersHandler(routersManager, extractFromToken, authCfg)
|
||||||
|
router.HandleFunc("/networks/{networkId}/routers", routersHandler.getAllRouters).Methods("GET", "OPTIONS")
|
||||||
|
router.HandleFunc("/networks/{networkId}/routers", routersHandler.createRouter).Methods("POST", "OPTIONS")
|
||||||
|
router.HandleFunc("/networks/{networkId}/routers/{routerId}", routersHandler.getRouter).Methods("GET", "OPTIONS")
|
||||||
|
router.HandleFunc("/networks/{networkId}/routers/{routerId}", routersHandler.updateRouter).Methods("PUT", "OPTIONS")
|
||||||
|
router.HandleFunc("/networks/{networkId}/routers/{routerId}", routersHandler.deleteRouter).Methods("DELETE", "OPTIONS")
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRoutersHandler(routersManager routers.Manager, extractFromToken func(ctx context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error), authCfg configs.AuthCfg) *routersHandler {
|
||||||
|
return &routersHandler{
|
||||||
|
routersManager: routersManager,
|
||||||
|
extractFromToken: extractFromToken,
|
||||||
|
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||||
|
jwtclaims.WithAudience(authCfg.Audience),
|
||||||
|
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *routersHandler) getAllRouters(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
networkID := mux.Vars(r)["networkId"]
|
||||||
|
routers, err := h.routersManager.GetAllRoutersInNetwork(r.Context(), accountID, userID, networkID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var routersResponse []*api.NetworkRouter
|
||||||
|
for _, router := range routers {
|
||||||
|
routersResponse = append(routersResponse, router.ToAPIResponse())
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(r.Context(), w, routersResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *routersHandler) createRouter(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
networkID := mux.Vars(r)["networkId"]
|
||||||
|
var req api.NetworkRouterRequest
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
router := &types.NetworkRouter{}
|
||||||
|
router.FromAPIRequest(&req)
|
||||||
|
|
||||||
|
router.NetworkID = networkID
|
||||||
|
router.AccountID = accountID
|
||||||
|
|
||||||
|
router, err = h.routersManager.CreateRouter(r.Context(), userID, router)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(r.Context(), w, router.ToAPIResponse())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *routersHandler) getRouter(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
routerID := mux.Vars(r)["routerId"]
|
||||||
|
networkID := mux.Vars(r)["networkId"]
|
||||||
|
router, err := h.routersManager.GetRouter(r.Context(), accountID, userID, networkID, routerID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(r.Context(), w, router.ToAPIResponse())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *routersHandler) updateRouter(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req api.NetworkRouterRequest
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
router := &types.NetworkRouter{}
|
||||||
|
router.FromAPIRequest(&req)
|
||||||
|
|
||||||
|
router.NetworkID = mux.Vars(r)["networkId"]
|
||||||
|
router.ID = mux.Vars(r)["routerId"]
|
||||||
|
router.AccountID = accountID
|
||||||
|
|
||||||
|
router, err = h.routersManager.UpdateRouter(r.Context(), userID, router)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(r.Context(), w, router.ToAPIResponse())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *routersHandler) deleteRouter(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
accountID, userID, err := h.extractFromToken(r.Context(), claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
routerID := mux.Vars(r)["routerId"]
|
||||||
|
networkID := mux.Vars(r)["networkId"]
|
||||||
|
err = h.routersManager.DeleteRouter(r.Context(), accountID, userID, networkID, routerID)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(r.Context(), err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(r.Context(), w, struct{}{})
|
||||||
|
}
|
@ -10,13 +10,14 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server"
|
"github.com/netbirdio/netbird/management/server"
|
||||||
nbgroup "github.com/netbirdio/netbird/management/server/group"
|
"github.com/netbirdio/netbird/management/server/groups"
|
||||||
"github.com/netbirdio/netbird/management/server/http/api"
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||||
"github.com/netbirdio/netbird/management/server/http/util"
|
"github.com/netbirdio/netbird/management/server/http/util"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handler is a handler that returns peers of the account
|
// Handler is a handler that returns peers of the account
|
||||||
@ -57,7 +58,7 @@ func (h *Handler) checkPeerStatus(peer *nbpeer.Peer) (*nbpeer.Peer, error) {
|
|||||||
return peerToReturn, nil
|
return peerToReturn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) getPeer(ctx context.Context, account *server.Account, peerID, userID string, w http.ResponseWriter) {
|
func (h *Handler) getPeer(ctx context.Context, account *types.Account, peerID, userID string, w http.ResponseWriter) {
|
||||||
peer, err := h.accountManager.GetPeer(ctx, account.Id, peerID, userID)
|
peer, err := h.accountManager.GetPeer(ctx, account.Id, peerID, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(ctx, err, w)
|
util.WriteError(ctx, err, w)
|
||||||
@ -71,7 +72,7 @@ func (h *Handler) getPeer(ctx context.Context, account *server.Account, peerID,
|
|||||||
}
|
}
|
||||||
dnsDomain := h.accountManager.GetDNSDomain()
|
dnsDomain := h.accountManager.GetDNSDomain()
|
||||||
|
|
||||||
groupsInfo := toGroupsInfo(account.Groups, peer.ID)
|
groupsInfo := groups.ToGroupsInfo(account.Groups, peer.ID)
|
||||||
|
|
||||||
validPeers, err := h.accountManager.GetValidatedPeers(account)
|
validPeers, err := h.accountManager.GetValidatedPeers(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -84,7 +85,7 @@ func (h *Handler) getPeer(ctx context.Context, account *server.Account, peerID,
|
|||||||
util.WriteJSONObject(ctx, w, toSinglePeerResponse(peerToReturn, groupsInfo, dnsDomain, valid))
|
util.WriteJSONObject(ctx, w, toSinglePeerResponse(peerToReturn, groupsInfo, dnsDomain, valid))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) updatePeer(ctx context.Context, account *server.Account, userID, peerID string, w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) updatePeer(ctx context.Context, account *types.Account, userID, peerID string, w http.ResponseWriter, r *http.Request) {
|
||||||
req := &api.PeerRequest{}
|
req := &api.PeerRequest{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&req)
|
err := json.NewDecoder(r.Body).Decode(&req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -115,7 +116,7 @@ func (h *Handler) updatePeer(ctx context.Context, account *server.Account, userI
|
|||||||
}
|
}
|
||||||
dnsDomain := h.accountManager.GetDNSDomain()
|
dnsDomain := h.accountManager.GetDNSDomain()
|
||||||
|
|
||||||
groupMinimumInfo := toGroupsInfo(account.Groups, peer.ID)
|
groupMinimumInfo := groups.ToGroupsInfo(account.Groups, peer.ID)
|
||||||
|
|
||||||
validPeers, err := h.accountManager.GetValidatedPeers(account)
|
validPeers, err := h.accountManager.GetValidatedPeers(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -199,9 +200,9 @@ func (h *Handler) GetAllPeers(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
groupsMap := map[string]*nbgroup.Group{}
|
groupsMap := map[string]*types.Group{}
|
||||||
groups, _ := h.accountManager.GetAllGroups(r.Context(), accountID, userID)
|
grps, _ := h.accountManager.GetAllGroups(r.Context(), accountID, userID)
|
||||||
for _, group := range groups {
|
for _, group := range grps {
|
||||||
groupsMap[group.ID] = group
|
groupsMap[group.ID] = group
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +213,7 @@ func (h *Handler) GetAllPeers(w http.ResponseWriter, r *http.Request) {
|
|||||||
util.WriteError(r.Context(), err, w)
|
util.WriteError(r.Context(), err, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
groupMinimumInfo := toGroupsInfo(groupsMap, peer.ID)
|
groupMinimumInfo := groups.ToGroupsInfo(groupsMap, peer.ID)
|
||||||
|
|
||||||
respBody = append(respBody, toPeerListItemResponse(peerToReturn, groupMinimumInfo, dnsDomain, 0))
|
respBody = append(respBody, toPeerListItemResponse(peerToReturn, groupMinimumInfo, dnsDomain, 0))
|
||||||
}
|
}
|
||||||
@ -290,12 +291,12 @@ func (h *Handler) GetAccessiblePeers(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
customZone := account.GetPeersCustomZone(r.Context(), h.accountManager.GetDNSDomain())
|
customZone := account.GetPeersCustomZone(r.Context(), h.accountManager.GetDNSDomain())
|
||||||
netMap := account.GetPeerNetworkMap(r.Context(), peerID, customZone, validPeers, nil)
|
netMap := account.GetPeerNetworkMap(r.Context(), peerID, customZone, validPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil)
|
||||||
|
|
||||||
util.WriteJSONObject(r.Context(), w, toAccessiblePeers(netMap, dnsDomain))
|
util.WriteJSONObject(r.Context(), w, toAccessiblePeers(netMap, dnsDomain))
|
||||||
}
|
}
|
||||||
|
|
||||||
func toAccessiblePeers(netMap *server.NetworkMap, dnsDomain string) []api.AccessiblePeer {
|
func toAccessiblePeers(netMap *types.NetworkMap, dnsDomain string) []api.AccessiblePeer {
|
||||||
accessiblePeers := make([]api.AccessiblePeer, 0, len(netMap.Peers)+len(netMap.OfflinePeers))
|
accessiblePeers := make([]api.AccessiblePeer, 0, len(netMap.Peers)+len(netMap.OfflinePeers))
|
||||||
for _, p := range netMap.Peers {
|
for _, p := range netMap.Peers {
|
||||||
accessiblePeers = append(accessiblePeers, peerToAccessiblePeer(p, dnsDomain))
|
accessiblePeers = append(accessiblePeers, peerToAccessiblePeer(p, dnsDomain))
|
||||||
@ -324,30 +325,6 @@ func peerToAccessiblePeer(peer *nbpeer.Peer, dnsDomain string) api.AccessiblePee
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toGroupsInfo(groups map[string]*nbgroup.Group, peerID string) []api.GroupMinimum {
|
|
||||||
groupsInfo := []api.GroupMinimum{}
|
|
||||||
groupsChecked := make(map[string]struct{})
|
|
||||||
for _, group := range groups {
|
|
||||||
_, ok := groupsChecked[group.ID]
|
|
||||||
if ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
groupsChecked[group.ID] = struct{}{}
|
|
||||||
for _, pk := range group.Peers {
|
|
||||||
if pk == peerID {
|
|
||||||
info := api.GroupMinimum{
|
|
||||||
Id: group.ID,
|
|
||||||
Name: group.Name,
|
|
||||||
PeersCount: len(group.Peers),
|
|
||||||
}
|
|
||||||
groupsInfo = append(groupsInfo, info)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return groupsInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsDomain string, approved bool) *api.Peer {
|
func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsDomain string, approved bool) *api.Peer {
|
||||||
osVersion := peer.Meta.OSVersion
|
osVersion := peer.Meta.OSVersion
|
||||||
if osVersion == "" {
|
if osVersion == "" {
|
||||||
|
@ -15,11 +15,10 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server"
|
|
||||||
nbgroup "github.com/netbirdio/netbird/management/server/group"
|
|
||||||
"github.com/netbirdio/netbird/management/server/http/api"
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
@ -73,18 +72,18 @@ func initTestMetaData(peers ...*nbpeer.Peer) *Handler {
|
|||||||
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
||||||
return claims.AccountId, claims.UserId, nil
|
return claims.AccountId, claims.UserId, nil
|
||||||
},
|
},
|
||||||
GetAccountByIDFunc: func(ctx context.Context, accountID string, userID string) (*server.Account, error) {
|
GetAccountByIDFunc: func(ctx context.Context, accountID string, userID string) (*types.Account, error) {
|
||||||
peersMap := make(map[string]*nbpeer.Peer)
|
peersMap := make(map[string]*nbpeer.Peer)
|
||||||
for _, peer := range peers {
|
for _, peer := range peers {
|
||||||
peersMap[peer.ID] = peer.Copy()
|
peersMap[peer.ID] = peer.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
policy := &server.Policy{
|
policy := &types.Policy{
|
||||||
ID: "policy",
|
ID: "policy",
|
||||||
AccountID: accountID,
|
AccountID: accountID,
|
||||||
Name: "policy",
|
Name: "policy",
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Rules: []*server.PolicyRule{
|
Rules: []*types.PolicyRule{
|
||||||
{
|
{
|
||||||
ID: "rule",
|
ID: "rule",
|
||||||
Name: "rule",
|
Name: "rule",
|
||||||
@ -99,19 +98,19 @@ func initTestMetaData(peers ...*nbpeer.Peer) *Handler {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
srvUser := server.NewRegularUser(serviceUser)
|
srvUser := types.NewRegularUser(serviceUser)
|
||||||
srvUser.IsServiceUser = true
|
srvUser.IsServiceUser = true
|
||||||
|
|
||||||
account := &server.Account{
|
account := &types.Account{
|
||||||
Id: accountID,
|
Id: accountID,
|
||||||
Domain: "hotmail.com",
|
Domain: "hotmail.com",
|
||||||
Peers: peersMap,
|
Peers: peersMap,
|
||||||
Users: map[string]*server.User{
|
Users: map[string]*types.User{
|
||||||
adminUser: server.NewAdminUser(adminUser),
|
adminUser: types.NewAdminUser(adminUser),
|
||||||
regularUser: server.NewRegularUser(regularUser),
|
regularUser: types.NewRegularUser(regularUser),
|
||||||
serviceUser: srvUser,
|
serviceUser: srvUser,
|
||||||
},
|
},
|
||||||
Groups: map[string]*nbgroup.Group{
|
Groups: map[string]*types.Group{
|
||||||
"group1": {
|
"group1": {
|
||||||
ID: "group1",
|
ID: "group1",
|
||||||
AccountID: accountID,
|
AccountID: accountID,
|
||||||
@ -120,12 +119,12 @@ func initTestMetaData(peers ...*nbpeer.Peer) *Handler {
|
|||||||
Peers: maps.Keys(peersMap),
|
Peers: maps.Keys(peersMap),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Settings: &server.Settings{
|
Settings: &types.Settings{
|
||||||
PeerLoginExpirationEnabled: true,
|
PeerLoginExpirationEnabled: true,
|
||||||
PeerLoginExpiration: time.Hour,
|
PeerLoginExpiration: time.Hour,
|
||||||
},
|
},
|
||||||
Policies: []*server.Policy{policy},
|
Policies: []*types.Policy{policy},
|
||||||
Network: &server.Network{
|
Network: &types.Network{
|
||||||
Identifier: "ciclqisab2ss43jdn8q0",
|
Identifier: "ciclqisab2ss43jdn8q0",
|
||||||
Net: net.IPNet{
|
Net: net.IPNet{
|
||||||
IP: net.ParseIP("100.67.0.0"),
|
IP: net.ParseIP("100.67.0.0"),
|
||||||
|
@ -13,11 +13,11 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server"
|
|
||||||
"github.com/netbirdio/netbird/management/server/geolocation"
|
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||||
"github.com/netbirdio/netbird/management/server/http/api"
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
"github.com/netbirdio/netbird/management/server/mock_server"
|
"github.com/netbirdio/netbird/management/server/mock_server"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -46,8 +46,8 @@ func initGeolocationTestData(t *testing.T) *geolocationsHandler {
|
|||||||
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
||||||
return claims.AccountId, claims.UserId, nil
|
return claims.AccountId, claims.UserId, nil
|
||||||
},
|
},
|
||||||
GetUserByIDFunc: func(ctx context.Context, id string) (*server.User, error) {
|
GetUserByIDFunc: func(ctx context.Context, id string) (*types.User, error) {
|
||||||
return server.NewAdminUser(id), nil
|
return types.NewAdminUser(id), nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
geolocationManager: geo,
|
geolocationManager: geo,
|
||||||
|
@ -9,12 +9,12 @@ import (
|
|||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server"
|
"github.com/netbirdio/netbird/management/server"
|
||||||
"github.com/netbirdio/netbird/management/server/geolocation"
|
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||||
nbgroup "github.com/netbirdio/netbird/management/server/group"
|
|
||||||
"github.com/netbirdio/netbird/management/server/http/api"
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
"github.com/netbirdio/netbird/management/server/http/configs"
|
"github.com/netbirdio/netbird/management/server/http/configs"
|
||||||
"github.com/netbirdio/netbird/management/server/http/util"
|
"github.com/netbirdio/netbird/management/server/http/util"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// handler is a handler that returns policy of the account
|
// handler is a handler that returns policy of the account
|
||||||
@ -133,7 +133,7 @@ func (h *handler) savePolicy(w http.ResponseWriter, r *http.Request, accountID s
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
policy := &server.Policy{
|
policy := &types.Policy{
|
||||||
ID: policyID,
|
ID: policyID,
|
||||||
AccountID: accountID,
|
AccountID: accountID,
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
@ -146,15 +146,56 @@ func (h *handler) savePolicy(w http.ResponseWriter, r *http.Request, accountID s
|
|||||||
ruleID = *rule.Id
|
ruleID = *rule.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
pr := server.PolicyRule{
|
hasSources := rule.Sources != nil
|
||||||
|
hasSourceResource := rule.SourceResource != nil
|
||||||
|
|
||||||
|
hasDestinations := rule.Destinations != nil
|
||||||
|
hasDestinationResource := rule.DestinationResource != nil
|
||||||
|
|
||||||
|
if hasSources && hasSourceResource {
|
||||||
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "specify either sources or source resources, not both"), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasDestinations && hasDestinationResource {
|
||||||
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "specify either destinations or destination resources, not both"), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(hasSources || hasSourceResource) || !(hasDestinations || hasDestinationResource) {
|
||||||
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "specify either sources or source resources and destinations or destination resources"), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pr := types.PolicyRule{
|
||||||
ID: ruleID,
|
ID: ruleID,
|
||||||
PolicyID: policyID,
|
PolicyID: policyID,
|
||||||
Name: rule.Name,
|
Name: rule.Name,
|
||||||
Destinations: rule.Destinations,
|
|
||||||
Sources: rule.Sources,
|
|
||||||
Bidirectional: rule.Bidirectional,
|
Bidirectional: rule.Bidirectional,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if hasSources {
|
||||||
|
pr.Sources = *rule.Sources
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasSourceResource {
|
||||||
|
// TODO: validate the resource id and type
|
||||||
|
sourceResource := &types.Resource{}
|
||||||
|
sourceResource.FromAPIRequest(rule.SourceResource)
|
||||||
|
pr.SourceResource = *sourceResource
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasDestinations {
|
||||||
|
pr.Destinations = *rule.Destinations
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasDestinationResource {
|
||||||
|
// TODO: validate the resource id and type
|
||||||
|
destinationResource := &types.Resource{}
|
||||||
|
destinationResource.FromAPIRequest(rule.DestinationResource)
|
||||||
|
pr.DestinationResource = *destinationResource
|
||||||
|
}
|
||||||
|
|
||||||
pr.Enabled = rule.Enabled
|
pr.Enabled = rule.Enabled
|
||||||
if rule.Description != nil {
|
if rule.Description != nil {
|
||||||
pr.Description = *rule.Description
|
pr.Description = *rule.Description
|
||||||
@ -162,9 +203,9 @@ func (h *handler) savePolicy(w http.ResponseWriter, r *http.Request, accountID s
|
|||||||
|
|
||||||
switch rule.Action {
|
switch rule.Action {
|
||||||
case api.PolicyRuleUpdateActionAccept:
|
case api.PolicyRuleUpdateActionAccept:
|
||||||
pr.Action = server.PolicyTrafficActionAccept
|
pr.Action = types.PolicyTrafficActionAccept
|
||||||
case api.PolicyRuleUpdateActionDrop:
|
case api.PolicyRuleUpdateActionDrop:
|
||||||
pr.Action = server.PolicyTrafficActionDrop
|
pr.Action = types.PolicyTrafficActionDrop
|
||||||
default:
|
default:
|
||||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "unknown action type"), w)
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "unknown action type"), w)
|
||||||
return
|
return
|
||||||
@ -172,13 +213,13 @@ func (h *handler) savePolicy(w http.ResponseWriter, r *http.Request, accountID s
|
|||||||
|
|
||||||
switch rule.Protocol {
|
switch rule.Protocol {
|
||||||
case api.PolicyRuleUpdateProtocolAll:
|
case api.PolicyRuleUpdateProtocolAll:
|
||||||
pr.Protocol = server.PolicyRuleProtocolALL
|
pr.Protocol = types.PolicyRuleProtocolALL
|
||||||
case api.PolicyRuleUpdateProtocolTcp:
|
case api.PolicyRuleUpdateProtocolTcp:
|
||||||
pr.Protocol = server.PolicyRuleProtocolTCP
|
pr.Protocol = types.PolicyRuleProtocolTCP
|
||||||
case api.PolicyRuleUpdateProtocolUdp:
|
case api.PolicyRuleUpdateProtocolUdp:
|
||||||
pr.Protocol = server.PolicyRuleProtocolUDP
|
pr.Protocol = types.PolicyRuleProtocolUDP
|
||||||
case api.PolicyRuleUpdateProtocolIcmp:
|
case api.PolicyRuleUpdateProtocolIcmp:
|
||||||
pr.Protocol = server.PolicyRuleProtocolICMP
|
pr.Protocol = types.PolicyRuleProtocolICMP
|
||||||
default:
|
default:
|
||||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "unknown protocol type: %v", rule.Protocol), w)
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "unknown protocol type: %v", rule.Protocol), w)
|
||||||
return
|
return
|
||||||
@ -205,7 +246,7 @@ func (h *handler) savePolicy(w http.ResponseWriter, r *http.Request, accountID s
|
|||||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "valid port value is in 1..65535 range"), w)
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "valid port value is in 1..65535 range"), w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pr.PortRanges = append(pr.PortRanges, server.RulePortRange{
|
pr.PortRanges = append(pr.PortRanges, types.RulePortRange{
|
||||||
Start: uint16(portRange.Start),
|
Start: uint16(portRange.Start),
|
||||||
End: uint16(portRange.End),
|
End: uint16(portRange.End),
|
||||||
})
|
})
|
||||||
@ -214,7 +255,7 @@ func (h *handler) savePolicy(w http.ResponseWriter, r *http.Request, accountID s
|
|||||||
|
|
||||||
// validate policy object
|
// validate policy object
|
||||||
switch pr.Protocol {
|
switch pr.Protocol {
|
||||||
case server.PolicyRuleProtocolALL, server.PolicyRuleProtocolICMP:
|
case types.PolicyRuleProtocolALL, types.PolicyRuleProtocolICMP:
|
||||||
if len(pr.Ports) != 0 || len(pr.PortRanges) != 0 {
|
if len(pr.Ports) != 0 || len(pr.PortRanges) != 0 {
|
||||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "for ALL or ICMP protocol ports is not allowed"), w)
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "for ALL or ICMP protocol ports is not allowed"), w)
|
||||||
return
|
return
|
||||||
@ -223,7 +264,7 @@ func (h *handler) savePolicy(w http.ResponseWriter, r *http.Request, accountID s
|
|||||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "for ALL or ICMP protocol type flow can be only bi-directional"), w)
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "for ALL or ICMP protocol type flow can be only bi-directional"), w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case server.PolicyRuleProtocolTCP, server.PolicyRuleProtocolUDP:
|
case types.PolicyRuleProtocolTCP, types.PolicyRuleProtocolUDP:
|
||||||
if !pr.Bidirectional && (len(pr.Ports) == 0 || len(pr.PortRanges) != 0) {
|
if !pr.Bidirectional && (len(pr.Ports) == 0 || len(pr.PortRanges) != 0) {
|
||||||
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "for ALL or ICMP protocol type flow can be only bi-directional"), w)
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "for ALL or ICMP protocol type flow can be only bi-directional"), w)
|
||||||
return
|
return
|
||||||
@ -319,8 +360,8 @@ func (h *handler) getPolicy(w http.ResponseWriter, r *http.Request) {
|
|||||||
util.WriteJSONObject(r.Context(), w, resp)
|
util.WriteJSONObject(r.Context(), w, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toPolicyResponse(groups []*nbgroup.Group, policy *server.Policy) *api.Policy {
|
func toPolicyResponse(groups []*types.Group, policy *types.Policy) *api.Policy {
|
||||||
groupsMap := make(map[string]*nbgroup.Group)
|
groupsMap := make(map[string]*types.Group)
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
groupsMap[group.ID] = group
|
groupsMap[group.ID] = group
|
||||||
}
|
}
|
||||||
@ -337,13 +378,15 @@ func toPolicyResponse(groups []*nbgroup.Group, policy *server.Policy) *api.Polic
|
|||||||
rID := r.ID
|
rID := r.ID
|
||||||
rDescription := r.Description
|
rDescription := r.Description
|
||||||
rule := api.PolicyRule{
|
rule := api.PolicyRule{
|
||||||
Id: &rID,
|
Id: &rID,
|
||||||
Name: r.Name,
|
Name: r.Name,
|
||||||
Enabled: r.Enabled,
|
Enabled: r.Enabled,
|
||||||
Description: &rDescription,
|
Description: &rDescription,
|
||||||
Bidirectional: r.Bidirectional,
|
Bidirectional: r.Bidirectional,
|
||||||
Protocol: api.PolicyRuleProtocol(r.Protocol),
|
Protocol: api.PolicyRuleProtocol(r.Protocol),
|
||||||
Action: api.PolicyRuleAction(r.Action),
|
Action: api.PolicyRuleAction(r.Action),
|
||||||
|
SourceResource: r.SourceResource.ToAPIResponse(),
|
||||||
|
DestinationResource: r.DestinationResource.ToAPIResponse(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(r.Ports) != 0 {
|
if len(r.Ports) != 0 {
|
||||||
@ -362,26 +405,30 @@ func toPolicyResponse(groups []*nbgroup.Group, policy *server.Policy) *api.Polic
|
|||||||
rule.PortRanges = &portRanges
|
rule.PortRanges = &portRanges
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sources []api.GroupMinimum
|
||||||
for _, gid := range r.Sources {
|
for _, gid := range r.Sources {
|
||||||
_, ok := cache[gid]
|
_, ok := cache[gid]
|
||||||
if ok {
|
if ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if group, ok := groupsMap[gid]; ok {
|
if group, ok := groupsMap[gid]; ok {
|
||||||
minimum := api.GroupMinimum{
|
minimum := api.GroupMinimum{
|
||||||
Id: group.ID,
|
Id: group.ID,
|
||||||
Name: group.Name,
|
Name: group.Name,
|
||||||
PeersCount: len(group.Peers),
|
PeersCount: len(group.Peers),
|
||||||
}
|
}
|
||||||
rule.Sources = append(rule.Sources, minimum)
|
sources = append(sources, minimum)
|
||||||
cache[gid] = minimum
|
cache[gid] = minimum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rule.Sources = &sources
|
||||||
|
|
||||||
|
var destinations []api.GroupMinimum
|
||||||
for _, gid := range r.Destinations {
|
for _, gid := range r.Destinations {
|
||||||
cachedMinimum, ok := cache[gid]
|
cachedMinimum, ok := cache[gid]
|
||||||
if ok {
|
if ok {
|
||||||
rule.Destinations = append(rule.Destinations, cachedMinimum)
|
destinations = append(destinations, cachedMinimum)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if group, ok := groupsMap[gid]; ok {
|
if group, ok := groupsMap[gid]; ok {
|
||||||
@ -390,10 +437,12 @@ func toPolicyResponse(groups []*nbgroup.Group, policy *server.Policy) *api.Polic
|
|||||||
Name: group.Name,
|
Name: group.Name,
|
||||||
PeersCount: len(group.Peers),
|
PeersCount: len(group.Peers),
|
||||||
}
|
}
|
||||||
rule.Destinations = append(rule.Destinations, minimum)
|
destinations = append(destinations, minimum)
|
||||||
cache[gid] = minimum
|
cache[gid] = minimum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rule.Destinations = &destinations
|
||||||
|
|
||||||
ap.Rules = append(ap.Rules, rule)
|
ap.Rules = append(ap.Rules, rule)
|
||||||
}
|
}
|
||||||
return ap
|
return ap
|
||||||
|
@ -10,9 +10,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
nbgroup "github.com/netbirdio/netbird/management/server/group"
|
|
||||||
"github.com/netbirdio/netbird/management/server/http/api"
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
@ -20,50 +20,49 @@ import (
|
|||||||
|
|
||||||
"github.com/magiconair/properties/assert"
|
"github.com/magiconair/properties/assert"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server"
|
|
||||||
"github.com/netbirdio/netbird/management/server/mock_server"
|
"github.com/netbirdio/netbird/management/server/mock_server"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initPoliciesTestData(policies ...*server.Policy) *handler {
|
func initPoliciesTestData(policies ...*types.Policy) *handler {
|
||||||
testPolicies := make(map[string]*server.Policy, len(policies))
|
testPolicies := make(map[string]*types.Policy, len(policies))
|
||||||
for _, policy := range policies {
|
for _, policy := range policies {
|
||||||
testPolicies[policy.ID] = policy
|
testPolicies[policy.ID] = policy
|
||||||
}
|
}
|
||||||
return &handler{
|
return &handler{
|
||||||
accountManager: &mock_server.MockAccountManager{
|
accountManager: &mock_server.MockAccountManager{
|
||||||
GetPolicyFunc: func(_ context.Context, _, policyID, _ string) (*server.Policy, error) {
|
GetPolicyFunc: func(_ context.Context, _, policyID, _ string) (*types.Policy, error) {
|
||||||
policy, ok := testPolicies[policyID]
|
policy, ok := testPolicies[policyID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, status.Errorf(status.NotFound, "policy not found")
|
return nil, status.Errorf(status.NotFound, "policy not found")
|
||||||
}
|
}
|
||||||
return policy, nil
|
return policy, nil
|
||||||
},
|
},
|
||||||
SavePolicyFunc: func(_ context.Context, _, _ string, policy *server.Policy) (*server.Policy, error) {
|
SavePolicyFunc: func(_ context.Context, _, _ string, policy *types.Policy) (*types.Policy, error) {
|
||||||
if !strings.HasPrefix(policy.ID, "id-") {
|
if !strings.HasPrefix(policy.ID, "id-") {
|
||||||
policy.ID = "id-was-set"
|
policy.ID = "id-was-set"
|
||||||
policy.Rules[0].ID = "id-was-set"
|
policy.Rules[0].ID = "id-was-set"
|
||||||
}
|
}
|
||||||
return policy, nil
|
return policy, nil
|
||||||
},
|
},
|
||||||
GetAllGroupsFunc: func(ctx context.Context, accountID, userID string) ([]*nbgroup.Group, error) {
|
GetAllGroupsFunc: func(ctx context.Context, accountID, userID string) ([]*types.Group, error) {
|
||||||
return []*nbgroup.Group{{ID: "F"}, {ID: "G"}}, nil
|
return []*types.Group{{ID: "F"}, {ID: "G"}}, nil
|
||||||
},
|
},
|
||||||
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
GetAccountIDFromTokenFunc: func(_ context.Context, claims jwtclaims.AuthorizationClaims) (string, string, error) {
|
||||||
return claims.AccountId, claims.UserId, nil
|
return claims.AccountId, claims.UserId, nil
|
||||||
},
|
},
|
||||||
GetAccountByIDFunc: func(ctx context.Context, accountID string, userID string) (*server.Account, error) {
|
GetAccountByIDFunc: func(ctx context.Context, accountID string, userID string) (*types.Account, error) {
|
||||||
user := server.NewAdminUser(userID)
|
user := types.NewAdminUser(userID)
|
||||||
return &server.Account{
|
return &types.Account{
|
||||||
Id: accountID,
|
Id: accountID,
|
||||||
Domain: "hotmail.com",
|
Domain: "hotmail.com",
|
||||||
Policies: []*server.Policy{
|
Policies: []*types.Policy{
|
||||||
{ID: "id-existed"},
|
{ID: "id-existed"},
|
||||||
},
|
},
|
||||||
Groups: map[string]*nbgroup.Group{
|
Groups: map[string]*types.Group{
|
||||||
"F": {ID: "F"},
|
"F": {ID: "F"},
|
||||||
"G": {ID: "G"},
|
"G": {ID: "G"},
|
||||||
},
|
},
|
||||||
Users: map[string]*server.User{
|
Users: map[string]*types.User{
|
||||||
"test_user": user,
|
"test_user": user,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
@ -105,10 +104,10 @@ func TestPoliciesGetPolicy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
policy := &server.Policy{
|
policy := &types.Policy{
|
||||||
ID: "idofthepolicy",
|
ID: "idofthepolicy",
|
||||||
Name: "Rule",
|
Name: "Rule",
|
||||||
Rules: []*server.PolicyRule{
|
Rules: []*types.PolicyRule{
|
||||||
{ID: "idoftherule", Name: "Rule"},
|
{ID: "idoftherule", Name: "Rule"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -177,7 +176,9 @@ func TestPoliciesWritePolicy(t *testing.T) {
|
|||||||
"Description": "Description",
|
"Description": "Description",
|
||||||
"Protocol": "tcp",
|
"Protocol": "tcp",
|
||||||
"Action": "accept",
|
"Action": "accept",
|
||||||
"Bidirectional":true
|
"Bidirectional":true,
|
||||||
|
"Sources": ["F"],
|
||||||
|
"Destinations": ["G"]
|
||||||
}
|
}
|
||||||
]}`)),
|
]}`)),
|
||||||
expectedStatus: http.StatusOK,
|
expectedStatus: http.StatusOK,
|
||||||
@ -193,6 +194,8 @@ func TestPoliciesWritePolicy(t *testing.T) {
|
|||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
Action: "accept",
|
Action: "accept",
|
||||||
Bidirectional: true,
|
Bidirectional: true,
|
||||||
|
Sources: &[]api.GroupMinimum{{Id: "F"}},
|
||||||
|
Destinations: &[]api.GroupMinimum{{Id: "G"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -221,7 +224,9 @@ func TestPoliciesWritePolicy(t *testing.T) {
|
|||||||
"Description": "Description",
|
"Description": "Description",
|
||||||
"Protocol": "tcp",
|
"Protocol": "tcp",
|
||||||
"Action": "accept",
|
"Action": "accept",
|
||||||
"Bidirectional":true
|
"Bidirectional":true,
|
||||||
|
"Sources": ["F"],
|
||||||
|
"Destinations": ["F"]
|
||||||
}
|
}
|
||||||
]}`)),
|
]}`)),
|
||||||
expectedStatus: http.StatusOK,
|
expectedStatus: http.StatusOK,
|
||||||
@ -237,6 +242,8 @@ func TestPoliciesWritePolicy(t *testing.T) {
|
|||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
Action: "accept",
|
Action: "accept",
|
||||||
Bidirectional: true,
|
Bidirectional: true,
|
||||||
|
Sources: &[]api.GroupMinimum{{Id: "F"}},
|
||||||
|
Destinations: &[]api.GroupMinimum{{Id: "F"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -251,10 +258,10 @@ func TestPoliciesWritePolicy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
p := initPoliciesTestData(&server.Policy{
|
p := initPoliciesTestData(&types.Policy{
|
||||||
ID: "id-existed",
|
ID: "id-existed",
|
||||||
Name: "Default POSTed Rule",
|
Name: "Default POSTed Rule",
|
||||||
Rules: []*server.PolicyRule{
|
Rules: []*types.PolicyRule{
|
||||||
{
|
{
|
||||||
ID: "id-existed",
|
ID: "id-existed",
|
||||||
Name: "Default POSTed Rule",
|
Name: "Default POSTed Rule",
|
||||||
|
@ -360,7 +360,7 @@ func validateDomains(domains []string) (domain.List, error) {
|
|||||||
return nil, fmt.Errorf("domains list exceeds maximum allowed domains: %d", maxDomains)
|
return nil, fmt.Errorf("domains list exceeds maximum allowed domains: %d", maxDomains)
|
||||||
}
|
}
|
||||||
|
|
||||||
domainRegex := regexp.MustCompile(`^(?:(?:xn--)?[a-zA-Z0-9_](?:[a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?\.)*(?:xn--)?[a-zA-Z0-9](?:[a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?$`)
|
domainRegex := regexp.MustCompile(`^(?:\*\.)?(?:(?:xn--)?[a-zA-Z0-9_](?:[a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?\.)*(?:xn--)?[a-zA-Z0-9](?:[a-zA-Z0-9-_]{0,61}[a-zA-Z0-9])?$`)
|
||||||
|
|
||||||
var domainList domain.List
|
var domainList domain.List
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user