Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
85
plugins/inputs/wireguard/README.md
Normal file
85
plugins/inputs/wireguard/README.md
Normal 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
|
||||
```
|
5
plugins/inputs/wireguard/sample.conf
Normal file
5
plugins/inputs/wireguard/sample.conf
Normal 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"]
|
142
plugins/inputs/wireguard/wireguard.go
Normal file
142
plugins/inputs/wireguard/wireguard.go
Normal 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{}
|
||||
})
|
||||
}
|
147
plugins/inputs/wireguard/wireguard_test.go
Normal file
147
plugins/inputs/wireguard/wireguard_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue