Nate Brown 4 дней назад
Родитель
Сommit
955caaf841
3 измененных файлов с 22 добавлено и 24 удалено
  1. 15 0
      config/config.go
  2. 2 23
      firewall.go
  3. 5 1
      interface.go

+ 15 - 0
config/config.go

@@ -86,6 +86,21 @@ func (c *C) InitialLoad() bool {
 	return c.oldSettings == nil
 }
 
+// Render returns keys and their values as yaml, for a subsystem to remember what it applied. Empty means they couldn't
+// be rendered and should never match.
+func (c *C) Render(keys ...string) string {
+	m := make(map[string]any, len(keys))
+	for _, k := range keys {
+		m[k] = c.Get(k)
+	}
+	b, err := yaml.Marshal(m)
+	if err != nil {
+		c.l.Debug("Failed to render config", "keys", keys, "error", err)
+		return ""
+	}
+	return string(b)
+}
+
 // HasChanged checks if the underlying structure of the provided key has changed after a config reload. The value of
 // k in both the old and new settings will be serialized, the result of the string comparison is returned.
 // If k is an empty string the entire config is tested.

+ 2 - 23
firewall.go

@@ -22,7 +22,6 @@ import (
 	"github.com/slackhq/nebula/config"
 	"github.com/slackhq/nebula/firewall"
 	"github.com/slackhq/nebula/iputil"
-	"go.yaml.in/yaml/v3"
 )
 
 type FirewallInterface interface {
@@ -67,7 +66,7 @@ type Firewall struct {
 	rules        string
 	rulesVersion uint16
 
-	// source is firewallSource for the config and cert this firewall was built from
+	// source is the rendered firewall config this firewall was built from, the cert networks are kept above
 	source string
 
 	defaultLocalCIDRAny bool
@@ -250,30 +249,10 @@ func NewFirewallFromConfig(l *slog.Logger, cs *CertState, c *config.C) (*Firewal
 		return nil, err
 	}
 
-	fw.source = firewallSource(l, c, certificate)
+	fw.source = c.Render("firewall")
 	return fw, nil
 }
 
-// firewallSource renders everything a firewall is built from, the firewall config and the cert's networks. A reload
-// compares it with what the running firewall was built from rather than with the last config, so a firewall that
-// failed to build is tried again on the next reload. Comparing a rendering stands in for comparing typed config. Empty
-// means it couldn't be rendered, which never matches
-func firewallSource(l *slog.Logger, c *config.C, certificate cert.Certificate) string {
-	if certificate == nil {
-		return ""
-	}
-	b, err := yaml.Marshal(map[string]any{
-		"firewall":        c.Get("firewall"),
-		"networks":        certificate.Networks(),
-		"unsafe_networks": certificate.UnsafeNetworks(),
-	})
-	if err != nil {
-		l.Debug("Failed to render the firewall config, the firewall is rebuilt on every reload until it renders", "error", err)
-		return ""
-	}
-	return string(b)
-}
-
 // AddRule properly creates the in memory rule structure for a firewall table.
 func (f *Firewall) AddRule(incoming bool, proto uint8, startPort int32, endPort int32, groups []string, host string, cidr, localCidr, caName string, caSha string) error {
 	var (

+ 5 - 1
interface.go

@@ -8,6 +8,7 @@ import (
 	"log/slog"
 	"net/netip"
 	"runtime"
+	"slices"
 	"sync"
 	"sync/atomic"
 	"time"
@@ -538,7 +539,10 @@ func (f *Interface) reloadFirewall(c *config.C, r *config.Reload) {
 
 	// Compare with what the running firewall was built from, not the last config, so a firewall that failed to build
 	// is tried again. The cert is the one this reload brought, PKI registers its reload callback before this one
-	if src := firewallSource(f.l, c, curCert); src != "" && src == f.firewall.source {
+	src := c.Render("firewall")
+	if src != "" && src == f.firewall.source && curCert != nil &&
+		slices.Equal(curCert.Networks(), f.firewall.assignedNetworks) &&
+		slices.Equal(curCert.UnsafeNetworks(), f.firewall.unsafeNetworks) {
 		f.l.Debug("No firewall config change detected")
 		return
 	}