Skip to content

Commit

Permalink
Merge pull request #73 from utilitywarehouse/prom-metrics-init
Browse files Browse the repository at this point in the history
Prom metrics init
  • Loading branch information
ffilippopoulos authored Jan 12, 2021
2 parents 23e8b2e + 896d4a1 commit d156793
Show file tree
Hide file tree
Showing 7 changed files with 712 additions and 12 deletions.
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ go 1.14
require (
github.com/coreos/go-iptables v0.4.5
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/golang/protobuf v1.4.2 // indirect
github.com/google/go-cmp v0.5.1
github.com/mdlayher/promtest v0.0.0-20200528141414-3c8577d47d5c
github.com/nirasan/go-oauth-pkce-code-verifier v0.0.0-20170819232839-0fbfe93532da
github.com/prometheus/client_golang v1.9.0
github.com/stretchr/testify v1.6.1
github.com/vishvananda/netlink v1.1.1-0.20200802231818-98629f7ffc4b
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect
golang.org/x/net v0.0.0-20200707034311-ab3426394381
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sys v0.0.0-20200805065543-0cf7623e9dbd
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e
golang.org/x/text v0.3.3 // indirect
golang.zx2c4.com/wireguard v0.0.20200320
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b
Expand Down
333 changes: 333 additions & 0 deletions go.sum

Large diffs are not rendered by default.

37 changes: 27 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@ import (
"os/signal"
"syscall"
"time"

"github.com/prometheus/client_golang/prometheus"
"golang.zx2c4.com/wireguard/wgctrl"
)

var (
version = "dev"
commit = "none"
date = "unknown"
builtBy = "unknown"
flagAgent = flag.Bool("agent", false, "Run application in \"agent\" mode")
flagConfig = flag.String("config", "/etc/wiresteward/config.json", "Config file")
flagLogLevel = flag.String("log-level", "info", "Log Level (debug|info|error)")
flagServer = flag.Bool("server", false, "Run application in \"server\" mode")
flagVersion = flag.Bool("version", false, "Prints out application version")
version = "dev"
commit = "none"
date = "unknown"
builtBy = "unknown"
flagAgent = flag.Bool("agent", false, "Run application in \"agent\" mode")
flagConfig = flag.String("config", "/etc/wiresteward/config.json", "Config file")
flagLogLevel = flag.String("log-level", "info", "Log Level (debug|info|error)")
flagMetricsAddr = flag.String("metrics-address", ":8081", "Metrics server address, meaningful when combined with -server flag")
flagServer = flag.Bool("server", false, "Run application in \"server\" mode")
flagVersion = flag.Bool("version", false, "Prints out application version")
)

func main() {
Expand Down Expand Up @@ -82,8 +86,21 @@ func server() {
if err != nil {
logger.Error.Fatalf("Cannot start lease server: %v", err)
}

tv := newTokenValidator(cfg.OauthClientID, cfg.OauthIntrospectURL)

// Start metrics server
client, err := wgctrl.New()
if err != nil {
logger.Error.Fatalf(
"Failed to open WireGuard control client: %v",
err,
)
}
defer client.Close()
mc := newMetricsCollector(client.Devices, lm)
prometheus.MustRegister(mc)
go startMetricsServer(*flagMetricsAddr)

lh := HTTPLeaseHandler{
leaseManager: lm,
serverConfig: cfg,
Expand Down
198 changes: 198 additions & 0 deletions metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package main

import (
"net/http"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)

// A collector is a prometheus.Collector for a WireGuard device.
type collector struct {
DeviceInfo *prometheus.Desc
PeerInfo *prometheus.Desc
PeerAllowedIPsInfo *prometheus.Desc
PeerReceiveBytes *prometheus.Desc
PeerTransmitBytes *prometheus.Desc
PeerLastHandshake *prometheus.Desc
PeerLeaseExpiryTime *prometheus.Desc

devices func() ([]*wgtypes.Device, error)
leaseManager *FileLeaseManager
}

// NewMetricsCollector constructs a prometheus.Collector to collect metrics for
// all present wg devices and correlate with user if possible
func newMetricsCollector(devices func() ([]*wgtypes.Device, error), lm *FileLeaseManager) prometheus.Collector {
// common labels for all metrics
labels := []string{"device", "public_key"}

return &collector{
DeviceInfo: prometheus.NewDesc(
"wiresteward_wg_device_info",
"Metadata about a device.",
labels,
nil,
),
PeerInfo: prometheus.NewDesc(
"wiresteward_wg_peer_info",
"Metadata about a peer. The public_key label on peer metrics refers to the peer's public key; not the device's public key.",
append(labels, []string{"username"}...),
nil,
),
PeerAllowedIPsInfo: prometheus.NewDesc(
"wiresteward_wg_peer_allowed_ips_info",
"Metadata about each of a peer's allowed IP subnets for a given device.",
append(labels, []string{"allowed_ips", "username"}...),
nil,
),
PeerReceiveBytes: prometheus.NewDesc(
"wiresteward_wg_peer_receive_bytes_total",
"Number of bytes received from a given peer.",
append(labels, "username"),
nil,
),
PeerTransmitBytes: prometheus.NewDesc(
"wiresteward_wg_peer_transmit_bytes_total",
"Number of bytes transmitted to a given peer.",
append(labels, "username"),
nil,
),
PeerLastHandshake: prometheus.NewDesc(
"wiresteward_wg_peer_last_handshake_seconds",
"UNIX timestamp for the last handshake with a given peer.",
append(labels, "username"),
nil,
),
PeerLeaseExpiryTime: prometheus.NewDesc(
"wiresteward_peer_lease_expiry_time",
"UNIX timestamp for the a peer's lease expiry time.",
[]string{"address", "public_key", "username"},
nil,
),
devices: devices,
leaseManager: lm,
}
}

// Describe implements prometheus.Collector.
func (c *collector) Describe(ch chan<- *prometheus.Desc) {
ds := []*prometheus.Desc{
c.DeviceInfo,
c.PeerInfo,
c.PeerAllowedIPsInfo,
c.PeerReceiveBytes,
c.PeerTransmitBytes,
c.PeerLastHandshake,
c.PeerLeaseExpiryTime,
}

for _, d := range ds {
ch <- d
}
}

// Collect implements prometheus.Collector.
func (c *collector) Collect(ch chan<- prometheus.Metric) {
devices, err := c.devices()
if err != nil {
logger.Error.Printf("Failed to list wg devices: %v", err)
ch <- prometheus.NewInvalidMetric(c.DeviceInfo, err)
return
}

for _, d := range devices {
ch <- prometheus.MustNewConstMetric(
c.DeviceInfo,
prometheus.GaugeValue,
1,
d.Name, d.PublicKey.String(),
)

for _, p := range d.Peers {
pub := p.PublicKey.String()
username := c.getUserFromPubKey(pub)

ch <- prometheus.MustNewConstMetric(
c.PeerInfo,
prometheus.GaugeValue,
1,
d.Name, pub, username,
)

for _, ip := range p.AllowedIPs {
ch <- prometheus.MustNewConstMetric(
c.PeerAllowedIPsInfo,
prometheus.GaugeValue,
1,
d.Name, pub, ip.String(), username,
)
}

ch <- prometheus.MustNewConstMetric(
c.PeerReceiveBytes,
prometheus.CounterValue,
float64(p.ReceiveBytes),
d.Name, pub, username,
)

ch <- prometheus.MustNewConstMetric(
c.PeerTransmitBytes,
prometheus.CounterValue,
float64(p.TransmitBytes),
d.Name, pub, username,
)

// Expose last handshake of 0 unless a last handshake time is set.
var last float64
if !p.LastHandshakeTime.IsZero() {
last = float64(p.LastHandshakeTime.Unix())
}

ch <- prometheus.MustNewConstMetric(
c.PeerLastHandshake,
prometheus.GaugeValue,
last,
d.Name, pub, username,
)
}
}
for username, record := range c.leaseManager.wgRecords {
// Expose expiry time of 0 if not set.
var expiry float64
if !record.expires.IsZero() {
expiry = float64(record.expires.Unix())
}

ch <- prometheus.MustNewConstMetric(
c.PeerLeaseExpiryTime,
prometheus.GaugeValue,
expiry,
record.IP.String(),
record.PubKey, username,
)
}
}

func (c *collector) getUserFromPubKey(pub string) string {
for username, wgRecord := range c.leaseManager.wgRecords {
if pub == wgRecord.PubKey {
return username
}
}
return ""
}

func startMetricsServer(metricsAddr string) {
mux := http.NewServeMux()
mux.Handle("/metrics", promhttp.Handler())
server := http.Server{
Addr: metricsAddr,
Handler: mux,
}
logger.Info.Printf("Starting metrics server at %s\n", metricsAddr)
if err := server.ListenAndServe(); err != nil {
logger.Error.Fatal(err)
}
}
Loading

0 comments on commit d156793

Please sign in to comment.