mirror of
https://github.com/slackhq/nebula.git
synced 2026-11-15 18:36:52 +01:00
Fail the reload when a config entry is skipped with a warning (#1952)
This commit is contained in:
1 parent
316bf87780
commit
a203d044bb
7 files changed
+91
-13
No files matched your search
+7
-5
@@ -316,9 +316,9 @@ type cachedPacketMetrics struct {
|
|||||||
func NewHostMapFromConfig(l *slog.Logger, c *config.C) *HostMap {
|
func NewHostMapFromConfig(l *slog.Logger, c *config.C) *HostMap {
|
||||||
hm := newHostMap(l)
|
hm := newHostMap(l)
|
||||||
|
|
||||||
hm.reload(c, true)
|
hm.reload(c, true, nil)
|
||||||
c.RegisterReloadCallback(func(c *config.C, _ *config.Reload) {
|
c.RegisterReloadCallback(func(c *config.C, r *config.Reload) {
|
||||||
hm.reload(c, false)
|
hm.reload(c, false, r)
|
||||||
})
|
})
|
||||||
|
|
||||||
l.Info("Main HostMap created", "preferredRanges", hm.GetPreferredRanges())
|
l.Info("Main HostMap created", "preferredRanges", hm.GetPreferredRanges())
|
||||||
@@ -337,7 +337,8 @@ func newHostMap(l *slog.Logger) *HostMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hm *HostMap) reload(c *config.C, initial bool) {
|
// r is nil on the initial load
|
||||||
|
func (hm *HostMap) reload(c *config.C, initial bool, r *config.Reload) {
|
||||||
if initial || c.HasChanged("preferred_ranges") {
|
if initial || c.HasChanged("preferred_ranges") {
|
||||||
var preferredRanges []netip.Prefix
|
var preferredRanges []netip.Prefix
|
||||||
rawPreferredRanges := c.GetStringSlice("preferred_ranges", []string{})
|
rawPreferredRanges := c.GetStringSlice("preferred_ranges", []string{})
|
||||||
@@ -348,8 +349,9 @@ func (hm *HostMap) reload(c *config.C, initial bool) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
hm.l.Warn("Failed to parse preferred ranges, ignoring",
|
hm.l.Warn("Failed to parse preferred ranges, ignoring",
|
||||||
"error", err,
|
"error", err,
|
||||||
"range", rawPreferredRanges,
|
"range", rawPreferredRange,
|
||||||
)
|
)
|
||||||
|
r.Failed()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -447,3 +447,16 @@ func TestHostInfo_markOutOnly(t *testing.T) {
|
|||||||
assert.True(t, h.markOut(6), "a relayed send must not consume the epoch edge")
|
assert.True(t, h.markOut(6), "a relayed send must not consume the epoch edge")
|
||||||
assert.False(t, h.markOut(6))
|
assert.False(t, h.markOut(6))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A preferred range that doesn't parse is skipped and the rest are used, the reload says one was skipped
|
||||||
|
func TestHostMap_reloadBadPreferredRangeFails(t *testing.T) {
|
||||||
|
l := test.NewLogger()
|
||||||
|
c := config.NewC(l)
|
||||||
|
require.NoError(t, c.LoadString("preferred_ranges: [10.0.0.0/8]\n"))
|
||||||
|
hm := NewHostMapFromConfig(l, c)
|
||||||
|
|
||||||
|
res := c.ReloadConfigString("preferred_ranges: [10.0.0.0/8, nope]\n")
|
||||||
|
require.NoError(t, res.Err)
|
||||||
|
assert.True(t, res.Failed)
|
||||||
|
assert.Equal(t, []netip.Prefix{netip.MustParsePrefix("10.0.0.0/8")}, hm.GetPreferredRanges())
|
||||||
|
}
|
||||||
+6
-3
@@ -138,14 +138,14 @@ func NewLightHouseFromConfig(ctx context.Context, l *slog.Logger, c *config.C, c
|
|||||||
h.metrics = newLighthouseMetrics()
|
h.metrics = newLighthouseMetrics()
|
||||||
}
|
}
|
||||||
|
|
||||||
err := h.reload(c, true)
|
err := h.reload(c, true, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.RegisterReloadCallback(func(c *config.C, r *config.Reload) {
|
c.RegisterReloadCallback(func(c *config.C, r *config.Reload) {
|
||||||
r.RestartRequiredIfChanged("lighthouse.am_lighthouse", "listen.port", "handshakes.query_buffer", "stats.lighthouse_metrics")
|
r.RestartRequiredIfChanged("lighthouse.am_lighthouse", "listen.port", "handshakes.query_buffer", "stats.lighthouse_metrics")
|
||||||
err := h.reload(c, false)
|
err := h.reload(c, false, r)
|
||||||
switch v := err.(type) {
|
switch v := err.(type) {
|
||||||
case *util.ContextualError:
|
case *util.ContextualError:
|
||||||
v.Log(l)
|
v.Log(l)
|
||||||
@@ -193,7 +193,8 @@ func (lh *LightHouse) GetUpdateInterval() int64 {
|
|||||||
return lh.interval.Load()
|
return lh.interval.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lh *LightHouse) reload(c *config.C, initial bool) error {
|
// r is nil on the initial load
|
||||||
|
func (lh *LightHouse) reload(c *config.C, initial bool, r *config.Reload) error {
|
||||||
if initial || c.HasChanged("lighthouse.advertise_addrs") {
|
if initial || c.HasChanged("lighthouse.advertise_addrs") {
|
||||||
rawAdvAddrs := c.GetStringSlice("lighthouse.advertise_addrs", []string{})
|
rawAdvAddrs := c.GetStringSlice("lighthouse.advertise_addrs", []string{})
|
||||||
advAddrs := make([]netip.AddrPort, 0)
|
advAddrs := make([]netip.AddrPort, 0)
|
||||||
@@ -228,6 +229,7 @@ func (lh *LightHouse) reload(c *config.C, initial bool) error {
|
|||||||
"addr", rawAddr,
|
"addr", rawAddr,
|
||||||
"entry", i+1,
|
"entry", i+1,
|
||||||
)
|
)
|
||||||
|
r.Failed()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,6 +388,7 @@ func (lh *LightHouse) reload(c *config.C, initial bool) error {
|
|||||||
"relay", v,
|
"relay", v,
|
||||||
"error", err,
|
"error", err,
|
||||||
)
|
)
|
||||||
|
r.Failed()
|
||||||
} else {
|
} else {
|
||||||
lh.l.Info("Read relay from config", "relay", v)
|
lh.l.Info("Read relay from config", "relay", v)
|
||||||
relaysForMe = append(relaysForMe, configRIP)
|
relaysForMe = append(relaysForMe, configRIP)
|
||||||
|
|||||||
+29
-1
@@ -296,7 +296,7 @@ func TestLighthouse_reload(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
c.ReloadConfigString(string(rc))
|
c.ReloadConfigString(string(rc))
|
||||||
|
|
||||||
err = lh.reload(c, false)
|
err = lh.reload(c, false, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1014,3 +1014,31 @@ func TestLighthouse_reloadRestartRequired(t *testing.T) {
|
|||||||
require.NoError(t, res.Err)
|
require.NoError(t, res.Err)
|
||||||
assert.True(t, res.RestartRequired)
|
assert.True(t, res.RestartRequired)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A relay that doesn't parse is skipped and the rest are used, the reload says one was skipped
|
||||||
|
func TestLighthouse_reloadBadRelayFails(t *testing.T) {
|
||||||
|
l := test.NewLogger()
|
||||||
|
c := config.NewC(l)
|
||||||
|
require.NoError(t, c.LoadString("listen:\n port: 4242\n"))
|
||||||
|
lh, err := NewLightHouseFromConfig(t.Context(), l, c, testCertState(netip.MustParsePrefix("10.128.0.1/24")), nil, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
res := c.ReloadConfigString("listen:\n port: 4242\nrelay:\n relays: [10.128.0.2, nope]\n")
|
||||||
|
require.NoError(t, res.Err)
|
||||||
|
assert.True(t, res.Failed)
|
||||||
|
assert.Equal(t, []netip.Addr{netip.MustParseAddr("10.128.0.2")}, lh.GetRelaysForMe())
|
||||||
|
}
|
||||||
|
|
||||||
|
// An advertise_addrs entry inside our own network is skipped and the rest are used, the reload says one was skipped
|
||||||
|
func TestLighthouse_reloadAdvertiseAddrInNetworkFails(t *testing.T) {
|
||||||
|
l := test.NewLogger()
|
||||||
|
c := config.NewC(l)
|
||||||
|
require.NoError(t, c.LoadString("listen:\n port: 4242\n"))
|
||||||
|
lh, err := NewLightHouseFromConfig(t.Context(), l, c, testCertState(netip.MustParsePrefix("10.128.0.1/24")), nil, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
res := c.ReloadConfigString("listen:\n port: 4242\nlighthouse:\n advertise_addrs: [\"1.2.3.4:4242\", \"10.128.0.5:4242\"]\n")
|
||||||
|
require.NoError(t, res.Err)
|
||||||
|
assert.True(t, res.Failed)
|
||||||
|
assert.Equal(t, []netip.AddrPort{netip.MustParseAddrPort("1.2.3.4:4242")}, lh.GetAdvertiseAddrs())
|
||||||
|
}
|
||||||
@@ -78,7 +78,7 @@ func Main(c *config.C, configTest bool, buildVersion string, l *slog.Logger, dev
|
|||||||
wireSSHReload(l, ssh, c)
|
wireSSHReload(l, ssh, c)
|
||||||
var sshStart func()
|
var sshStart func()
|
||||||
if c.GetBool("sshd.enabled", false) {
|
if c.GetBool("sshd.enabled", false) {
|
||||||
sshStart, err = configSSH(l, ssh, c)
|
sshStart, err = configSSH(l, ssh, c, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Warn("Failed to configure sshd, ssh debugging will not be available", "error", err)
|
l.Warn("Failed to configure sshd, ssh debugging will not be available", "error", err)
|
||||||
sshStart = nil
|
sshStart = nil
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ func wireSSHReload(l *slog.Logger, ssh *sshd.SSHServer, c *config.C) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sshRun, err := configSSH(l, ssh, c)
|
sshRun, err := configSSH(l, ssh, c, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Error("Failed to reconfigure the sshd", "error", err)
|
l.Error("Failed to reconfigure the sshd", "error", err)
|
||||||
ssh.Stop()
|
ssh.Stop()
|
||||||
@@ -117,7 +117,8 @@ func loadSSHHostKey(c *config.C) ([]byte, error) {
|
|||||||
// updates the passed-in SSHServer. On success, it returns a function
|
// updates the passed-in SSHServer. On success, it returns a function
|
||||||
// that callers may invoke to run the configured ssh server. On
|
// that callers may invoke to run the configured ssh server. On
|
||||||
// failure, it returns nil, error.
|
// failure, it returns nil, error.
|
||||||
func configSSH(l *slog.Logger, ssh *sshd.SSHServer, c *config.C) (func(), error) {
|
// r is nil at startup, entries that are skipped with a warning fail it on a reload
|
||||||
|
func configSSH(l *slog.Logger, ssh *sshd.SSHServer, c *config.C, r *config.Reload) (func(), error) {
|
||||||
listen := c.GetString("sshd.listen", "")
|
listen := c.GetString("sshd.listen", "")
|
||||||
if listen == "" {
|
if listen == "" {
|
||||||
return nil, fmt.Errorf("sshd.listen must be provided")
|
return nil, fmt.Errorf("sshd.listen must be provided")
|
||||||
@@ -150,6 +151,7 @@ func configSSH(l *slog.Logger, ssh *sshd.SSHServer, c *config.C) (func(), error)
|
|||||||
err := ssh.AddTrustedCA(caAuthorizedKey)
|
err := ssh.AddTrustedCA(caAuthorizedKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Warn("SSH CA had an error, ignoring", "error", err, "sshCA", caAuthorizedKey)
|
l.Warn("SSH CA had an error, ignoring", "error", err, "sshCA", caAuthorizedKey)
|
||||||
|
r.Failed()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,12 +163,14 @@ func configSSH(l *slog.Logger, ssh *sshd.SSHServer, c *config.C) (func(), error)
|
|||||||
kDef, ok := rk.(map[string]any)
|
kDef, ok := rk.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
l.Warn("Authorized user had an error, ignoring", "sshKeyConfig", rk)
|
l.Warn("Authorized user had an error, ignoring", "sshKeyConfig", rk)
|
||||||
|
r.Failed()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
user, ok := kDef["user"].(string)
|
user, ok := kDef["user"].(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
l.Warn("Authorized user is missing the user field", "sshKeyConfig", rk)
|
l.Warn("Authorized user is missing the user field", "sshKeyConfig", rk)
|
||||||
|
r.Failed()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,6 +184,7 @@ func configSSH(l *slog.Logger, ssh *sshd.SSHServer, c *config.C) (func(), error)
|
|||||||
"sshKeyConfig", rk,
|
"sshKeyConfig", rk,
|
||||||
"sshKey", v,
|
"sshKey", v,
|
||||||
)
|
)
|
||||||
|
r.Failed()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,6 +196,7 @@ func configSSH(l *slog.Logger, ssh *sshd.SSHServer, c *config.C) (func(), error)
|
|||||||
"sshKeyConfig", rk,
|
"sshKeyConfig", rk,
|
||||||
"sshKey", subK,
|
"sshKey", subK,
|
||||||
)
|
)
|
||||||
|
r.Failed()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,12 +206,14 @@ func configSSH(l *slog.Logger, ssh *sshd.SSHServer, c *config.C) (func(), error)
|
|||||||
"error", err,
|
"error", err,
|
||||||
"sshKeyConfig", sk,
|
"sshKeyConfig", sk,
|
||||||
)
|
)
|
||||||
|
r.Failed()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
l.Warn("Authorized user is missing the keys field or was not understood", "sshKeyConfig", rk)
|
l.Warn("Authorized user is missing the keys field or was not understood", "sshKeyConfig", rk)
|
||||||
|
r.Failed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+25
-1
@@ -47,7 +47,7 @@ func newSSHDReloadHarness(t *testing.T) *sshdReloadHarness {
|
|||||||
h.server, err = sshd.NewSSHServer(t.Context(), l)
|
h.server, err = sshd.NewSSHServer(t.Context(), l)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
wireSSHReload(l, h.server, h.c)
|
wireSSHReload(l, h.server, h.c)
|
||||||
run, err := configSSH(l, h.server, h.c)
|
run, err := configSSH(l, h.server, h.c, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
go run()
|
go run()
|
||||||
t.Cleanup(h.server.Stop)
|
t.Cleanup(h.server.Stop)
|
||||||
@@ -226,3 +226,27 @@ func TestSSHDReload_BadHostKeyFileStops(t *testing.T) {
|
|||||||
assert.True(t, h.c.ReloadConfigString(h.config()).Failed)
|
assert.True(t, h.c.ReloadConfigString(h.config()).Failed)
|
||||||
require.Eventually(t, func() bool { return !h.server.Running() }, 5*time.Second, 10*time.Millisecond)
|
require.Eventually(t, func() bool { return !h.server.Running() }, 5*time.Second, 10*time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A trusted CA or authorized user that is skipped with a warning fails the reload, the valid ones are still used
|
||||||
|
func TestSSHDReload_SkippedEntriesFail(t *testing.T) {
|
||||||
|
cases := map[string]string{
|
||||||
|
"bad trusted ca": " trusted_cas: [nope]\n",
|
||||||
|
"user entry not a map": " - nope\n",
|
||||||
|
"user missing": " - keys: [nope]\n",
|
||||||
|
"bad key": " - user: other\n keys: nope\n",
|
||||||
|
"key in a list not a string": " - user: other\n keys: [5]\n",
|
||||||
|
"bad key in a list": " - user: other\n keys: [nope]\n",
|
||||||
|
"keys missing": " - user: other\n",
|
||||||
|
}
|
||||||
|
for name, extra := range cases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
h := newSSHDReloadHarness(t)
|
||||||
|
h.extra = extra
|
||||||
|
res := h.c.ReloadConfigString(h.config())
|
||||||
|
require.NoError(t, res.Err)
|
||||||
|
assert.True(t, res.Failed)
|
||||||
|
require.Eventually(t, h.server.Running, 5*time.Second, 10*time.Millisecond)
|
||||||
|
h.connect(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user