Skip to content

Commit 2c4940a

Browse files
committed
fix: drop noisy WireGuard handshake warnings for offline peers
Omni keeps a WireGuard peer for every registered machine, so wireguard-go warns about a handshake it cannot send about every four seconds per offline machine. The Link resource already reports whether a machine is connected, so the warning adds nothing. The logger handed to the device now runs through logging.DropMessagesContaining, which discards entries whose message matches a given substring. Signed-off-by: Oguz Kilcan <oguz.kilcan@siderolabs.com>
1 parent 89d4185 commit 2c4940a

4 files changed

Lines changed: 100 additions & 1 deletion

File tree

internal/backend/logging/logging.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
package logging
88

99
import (
10+
"slices"
11+
"strings"
12+
1013
"go.uber.org/zap"
1114
"go.uber.org/zap/zapcore"
1215
)
@@ -27,3 +30,33 @@ func IncreaseLevel(logger *zap.Logger, lvl zapcore.Level) *zap.Logger {
2730

2831
return logger.WithOptions(zap.IncreaseLevel(lvl))
2932
}
33+
34+
// DropMessagesContaining discards entries whose message contains any of the given substrings.
35+
func DropMessagesContaining(logger *zap.Logger, substrings ...string) *zap.Logger {
36+
if len(substrings) == 0 {
37+
return logger
38+
}
39+
40+
return logger.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core {
41+
return &dropMessagesCore{Core: core, substrings: substrings}
42+
}))
43+
}
44+
45+
type dropMessagesCore struct {
46+
zapcore.Core
47+
48+
substrings []string
49+
}
50+
51+
func (c *dropMessagesCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
52+
if slices.ContainsFunc(c.substrings, func(substring string) bool { return strings.Contains(ent.Message, substring) }) {
53+
return ce
54+
}
55+
56+
return c.Core.Check(ent, ce)
57+
}
58+
59+
// With has to rewrap, otherwise a logger.With call inside the library escapes the filter.
60+
func (c *dropMessagesCore) With(fields []zapcore.Field) zapcore.Core {
61+
return &dropMessagesCore{Core: c.Core.With(fields), substrings: c.substrings}
62+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright (c) 2026 Sidero Labs, Inc.
2+
//
3+
// Use of this software is governed by the Business Source License
4+
// included in the LICENSE file.
5+
6+
package logging_test
7+
8+
import (
9+
"testing"
10+
11+
"github.com/stretchr/testify/require"
12+
"go.uber.org/zap"
13+
"go.uber.org/zap/zapcore"
14+
"go.uber.org/zap/zaptest/observer"
15+
16+
"github.com/siderolabs/omni/internal/backend/logging"
17+
)
18+
19+
func TestDropMessagesContaining(t *testing.T) {
20+
core, logs := observer.New(zapcore.DebugLevel)
21+
logger := logging.DropMessagesContaining(zap.New(core), "no known endpoint for peer", "MTU not updated")
22+
23+
logger.Warn("peer(abcd…wxyz) - Failed to send handshake initiation: no known endpoint for peer")
24+
logger.With(zap.String("component", "siderolink")).
25+
Warn("peer(abcd…wxyz) - Failed to send data packets: no known endpoint for peer")
26+
logger.Warn("MTU not updated to negative value: -1")
27+
logger.Warn("peer(abcd…wxyz) - Failed to send handshake initiation: write udp: connection refused")
28+
logger.Info("wireguard device set up")
29+
30+
entries := logs.All()
31+
32+
require.Len(t, entries, 2)
33+
require.Equal(t, "peer(abcd…wxyz) - Failed to send handshake initiation: write udp: connection refused", entries[0].Message)
34+
require.Equal(t, "wireguard device set up", entries[1].Message)
35+
36+
// the filter must leave the reported level alone
37+
require.Equal(t, zapcore.DebugLevel, logger.Level())
38+
}
39+
40+
func TestDropMessagesContainingWithoutSubstrings(t *testing.T) {
41+
core, logs := observer.New(zapcore.DebugLevel)
42+
logger := zap.New(core)
43+
44+
require.Same(t, logger, logging.DropMessagesContaining(logger))
45+
46+
logger.Warn("nothing is filtered")
47+
48+
require.Equal(t, 1, logs.Len())
49+
}

internal/pkg/siderolink/manager.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ func (manager *Manager) startWireguard(ctx context.Context, eg *errgroup.Group,
489489
}
490490
}()
491491

492-
return manager.wgHandler.Run(ctx, logging.IncreaseLevel(manager.logger, zap.InfoLevel))
492+
return manager.wgHandler.Run(ctx, wireguardLogger(manager.logger))
493493
})
494494

495495
return nil

internal/pkg/siderolink/wireguard.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,25 @@ import (
1616
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
1717

1818
"github.com/siderolabs/omni/client/api/omni/specs"
19+
"github.com/siderolabs/omni/internal/backend/logging"
1920
)
2021

22+
// noKnownEndpointError is what wireguard-go reports when it has to send to a peer it has never heard from.
23+
//
24+
// It comes from an inline errors.New, so there is no sentinel to compare against.
25+
const noKnownEndpointError = "no known endpoint for peer"
26+
27+
// wireguardLogger builds the logger for the WireGuard device.
28+
//
29+
// A peer keeps its configuration while its machine is offline, so every keepalive-triggered handshake
30+
// attempt fails until that machine dials in, and wireguard-go reports each failure. That works out to
31+
// about one warning every four seconds per offline machine, which buries everything else once a few of
32+
// them pile up. Nothing is lost by dropping them, since the Link resource already reports whether a
33+
// machine is connected.
34+
func wireguardLogger(logger *zap.Logger) *zap.Logger {
35+
return logging.DropMessagesContaining(logging.IncreaseLevel(logger, zap.InfoLevel), noKnownEndpointError)
36+
}
37+
2138
// WireguardHandler abstraction around peer handler and wgDevice.
2239
type WireguardHandler interface {
2340
SetupDevice(wireguard.DeviceConfig) error

0 commit comments

Comments
 (0)