1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View file

@ -0,0 +1,85 @@
# Wireguard Input Plugin
The Wireguard input plugin collects statistics on the local Wireguard server
using the [`wgctrl`](https://github.com/WireGuard/wgctrl-go) library. It
reports gauge metrics for Wireguard interface device(s) and its peers.
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
In addition to the plugin-specific configuration settings, plugins support
additional global and plugin configuration settings. These settings are used to
modify metrics, tags, and field or create aliases and configure ordering, etc.
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
## Configuration
```toml @sample.conf
# Collect Wireguard server interface and peer statistics
[[inputs.wireguard]]
## Optional list of Wireguard device/interface names to query.
## If omitted, all Wireguard interfaces are queried.
# devices = ["wg0"]
```
## Metrics
- `wireguard_device`
- tags:
- `name` (interface device name, e.g. `wg0`)
- `type` (Wireguard tunnel type, e.g. `linux_kernel` or `userspace`)
- fields:
- `listen_port` (int, UDP port on which the interface is listening)
- `firewall_mark` (int, device's current firewall mark)
- `peers` (int, number of peers associated with the device)
- `wireguard_peer`
- tags:
- `device` (associated interface device name, e.g. `wg0`)
- `public_key` (peer public key, e.g. `NZTRIrv/ClTcQoNAnChEot+WL7OH7uEGQmx8oAN9rWE=`)
- fields:
- `persistent_keepalive_interval_ns` (int, keepalive interval in
nanoseconds; 0 if unset)
- `protocol_version` (int, Wireguard protocol version number)
- `allowed_ips` (int, number of allowed IPs for this peer)
- `last_handshake_time_ns` (int, Unix timestamp of the last handshake for
this peer in nanoseconds)
- `rx_bytes` (int, number of bytes received from this peer)
- `tx_bytes` (int, number of bytes transmitted to this peer)
- `allowed_peer_cidr` (string, comma separated list of allowed peer CIDRs)
## Troubleshooting
### Error: `operation not permitted`
When the kernelspace implementation of Wireguard is in use (as opposed to its
userspace implementations), Telegraf communicates with the module over netlink.
This requires Telegraf to either run as root, or for the Telegraf binary to
have the `CAP_NET_ADMIN` capability.
To add this capability to the Telegraf binary (to allow this communication under
the default user `telegraf`):
```bash
sudo setcap CAP_NET_ADMIN+epi $(which telegraf)
```
N.B.: This capability is a filesystem attribute on the binary itself. The
attribute needs to be re-applied if the Telegraf binary is rotated (e.g.
on installation of new a Telegraf version from the system package manager).
### Error: `error enumerating Wireguard devices`
This usually happens when the device names specified in config are invalid.
Ensure that `sudo wg show` succeeds, and that the device names in config match
those printed by this command.
## Example Output
```text
wireguard_device,host=WGVPN,name=wg0,type=linux_kernel firewall_mark=51820i,listen_port=58216i 1582513589000000000
wireguard_device,host=WGVPN,name=wg0,type=linux_kernel peers=1i 1582513589000000000
wireguard_peer,device=wg0,host=WGVPN,public_key=NZTRIrv/ClTcQoNAnChEot+WL7OH7uEGQmx8oAN9rWE= allowed_ips=2i,persistent_keepalive_interval_ns=60000000000i,protocol_version=1i,allowed_peer_cidr=192.168.1.0/24,10.0.0.0/8 1582513589000000000
wireguard_peer,device=wg0,host=WGVPN,public_key=NZTRIrv/ClTcQoNAnChEot+WL7OH7uEGQmx8oAN9rWE= last_handshake_time_ns=1582513584530013376i,rx_bytes=6484i,tx_bytes=13540i 1582513589000000000
```

View file

@ -0,0 +1,5 @@
# Collect Wireguard server interface and peer statistics
[[inputs.wireguard]]
## Optional list of Wireguard device/interface names to query.
## If omitted, all Wireguard interfaces are queried.
# devices = ["wg0"]

View file

@ -0,0 +1,142 @@
//go:generate ../../../tools/readme_config_includer/generator
package wireguard
import (
_ "embed"
"fmt"
"strings"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs"
)
//go:embed sample.conf
var sampleConfig string
var (
deviceTypeNames = map[wgtypes.DeviceType]string{
wgtypes.Unknown: "unknown",
wgtypes.LinuxKernel: "linux_kernel",
wgtypes.Userspace: "userspace",
}
)
const (
measurementDevice = "wireguard_device"
measurementPeer = "wireguard_peer"
)
type Wireguard struct {
Devices []string `toml:"devices"`
Log telegraf.Logger `toml:"-"`
client *wgctrl.Client
}
func (*Wireguard) SampleConfig() string {
return sampleConfig
}
func (wg *Wireguard) Init() error {
var err error
wg.client, err = wgctrl.New()
return err
}
func (wg *Wireguard) Gather(acc telegraf.Accumulator) error {
devices, err := wg.enumerateDevices()
if err != nil {
return fmt.Errorf("error enumerating Wireguard devices: %w", err)
}
for _, device := range devices {
gatherDeviceMetrics(acc, device)
for _, peer := range device.Peers {
gatherDevicePeerMetrics(acc, device, peer)
}
}
return nil
}
func (wg *Wireguard) enumerateDevices() ([]*wgtypes.Device, error) {
// If no device names are specified, defer to the library to enumerate
// all of them
if len(wg.Devices) == 0 {
return wg.client.Devices()
}
// Otherwise, explicitly populate only device names specified in config
devices := make([]*wgtypes.Device, 0, len(wg.Devices))
for _, name := range wg.Devices {
dev, err := wg.client.Device(name)
if err != nil {
wg.Log.Warnf("No Wireguard device found with name %s", name)
continue
}
devices = append(devices, dev)
}
return devices, nil
}
func gatherDeviceMetrics(acc telegraf.Accumulator, device *wgtypes.Device) {
fields := map[string]interface{}{
"listen_port": device.ListenPort,
"firewall_mark": device.FirewallMark,
}
gauges := map[string]interface{}{
"peers": len(device.Peers),
}
tags := map[string]string{
"name": device.Name,
"type": deviceTypeNames[device.Type],
}
acc.AddFields(measurementDevice, fields, tags)
acc.AddGauge(measurementDevice, gauges, tags)
}
func gatherDevicePeerMetrics(acc telegraf.Accumulator, device *wgtypes.Device, peer wgtypes.Peer) {
fields := map[string]interface{}{
"persistent_keepalive_interval_ns": peer.PersistentKeepaliveInterval.Nanoseconds(),
"protocol_version": peer.ProtocolVersion,
"allowed_ips": len(peer.AllowedIPs),
}
if len(peer.AllowedIPs) > 0 {
cidrs := make([]string, 0, len(peer.AllowedIPs))
for _, ip := range peer.AllowedIPs {
cidrs = append(cidrs, ip.String())
}
fields["allowed_peer_cidr"] = strings.Join(cidrs, ",")
}
gauges := map[string]interface{}{
"last_handshake_time_ns": peer.LastHandshakeTime.UnixNano(),
"rx_bytes": peer.ReceiveBytes,
"tx_bytes": peer.TransmitBytes,
}
tags := map[string]string{
"device": device.Name,
"public_key": peer.PublicKey.String(),
}
acc.AddFields(measurementPeer, fields, tags)
acc.AddGauge(measurementPeer, gauges, tags)
}
func init() {
inputs.Add("wireguard", func() telegraf.Input {
return &Wireguard{}
})
}

View file

@ -0,0 +1,147 @@
package wireguard
import (
"net"
"testing"
"time"
"github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/influxdata/telegraf/testutil"
)
func TestWireguard_gatherDeviceMetrics(t *testing.T) {
device := &wgtypes.Device{
Name: "wg0",
Type: wgtypes.LinuxKernel,
ListenPort: 1,
FirewallMark: 2,
Peers: []wgtypes.Peer{{}, {}},
}
expectFields := map[string]interface{}{
"listen_port": 1,
"firewall_mark": 2,
}
expectGauges := map[string]interface{}{
"peers": 2,
}
expectTags := map[string]string{
"name": "wg0",
"type": "linux_kernel",
}
var acc testutil.Accumulator
gatherDeviceMetrics(&acc, device)
require.Equal(t, 3, acc.NFields())
acc.AssertDoesNotContainMeasurement(t, measurementPeer)
acc.AssertContainsTaggedFields(t, measurementDevice, expectFields, expectTags)
acc.AssertContainsTaggedFields(t, measurementDevice, expectGauges, expectTags)
}
func TestWireguard_gatherDevicePeerMetrics(t *testing.T) {
pubkey, err := wgtypes.ParseKey("NZTRIrv/ClTcQoNAnChEot+WL7OH7uEGQmx8oAN9rWE=")
require.NoError(t, err)
device := &wgtypes.Device{
Name: "wg0",
}
peer := wgtypes.Peer{
PublicKey: pubkey,
PersistentKeepaliveInterval: 1 * time.Minute,
LastHandshakeTime: time.Unix(100, 0),
ReceiveBytes: int64(40),
TransmitBytes: int64(60),
AllowedIPs: []net.IPNet{{}, {}},
ProtocolVersion: 0,
}
expectFields := map[string]interface{}{
"persistent_keepalive_interval_ns": int64(60000000000),
"protocol_version": 0,
"allowed_ips": 2,
"allowed_peer_cidr": "<nil>,<nil>",
}
expectGauges := map[string]interface{}{
"last_handshake_time_ns": int64(100000000000),
"rx_bytes": int64(40),
"tx_bytes": int64(60),
}
expectTags := map[string]string{
"device": "wg0",
"public_key": pubkey.String(),
}
var acc testutil.Accumulator
gatherDevicePeerMetrics(&acc, device, peer)
require.Equal(t, 7, acc.NFields())
acc.AssertDoesNotContainMeasurement(t, measurementDevice)
acc.AssertContainsTaggedFields(t, measurementPeer, expectFields, expectTags)
acc.AssertContainsTaggedFields(t, measurementPeer, expectGauges, expectTags)
}
func TestWireguard_allowedPeerCIDR(t *testing.T) {
var testcases = []struct {
name string
allowedIPs []net.IPNet
allowedPeerCidr string
}{
{
"single address",
[]net.IPNet{{
IP: net.IPv4(192, 168, 1, 0),
Mask: net.CIDRMask(20, 32),
}},
"192.168.1.0/20",
},
{
"multiple addresses",
[]net.IPNet{
{
IP: net.IPv4(10, 0, 0, 0),
Mask: net.CIDRMask(8, 32),
},
{
IP: net.IPv4(192, 168, 2, 0),
Mask: net.CIDRMask(24, 32),
},
},
"10.0.0.0/8,192.168.2.0/24",
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
pubkey, err := wgtypes.ParseKey("NZTRIrv/ClTcQoNAnChEot+WL7OH7uEGQmx8oAN9rWE=")
require.NoError(t, err)
device := &wgtypes.Device{
Name: "wg0",
}
peer := wgtypes.Peer{
PublicKey: pubkey,
PersistentKeepaliveInterval: 1 * time.Minute,
LastHandshakeTime: time.Unix(100, 0),
ReceiveBytes: int64(40),
TransmitBytes: int64(60),
AllowedIPs: tc.allowedIPs,
ProtocolVersion: 0,
}
expectFields := map[string]interface{}{
"persistent_keepalive_interval_ns": int64(60000000000),
"protocol_version": 0,
"allowed_ips": len(tc.allowedIPs),
"allowed_peer_cidr": tc.allowedPeerCidr,
}
_ = map[string]string{
"device": "wg0",
"public_key": pubkey.String(),
}
var acc testutil.Accumulator
gatherDevicePeerMetrics(&acc, device, peer)
acc.AssertDoesNotContainMeasurement(t, measurementDevice)
acc.AssertContainsFields(t, measurementPeer, expectFields)
})
}
}