Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add allow-duplicate-cn command-line flag #18

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ GLOBAL OPTIONS:
--web.root value Root path to exporter endpoints (default: "/") [$OPENVPN_EXPORTER_WEB_ROOT]
--status-file value The OpenVPN status file(s) to export (example test:./example/version1.status ) [$OPENVPN_EXPORTER_STATUS_FILE]
--disable-client-metrics Disables per client (bytes_received, bytes_sent, connected_since) metrics (default: false) [$OPENVPN_EXPORTER_DISABLE_CLIENT_METRICS]
--allow-duplicate-cn Allow multiple connections with the same common name distinguished by the Peer ID (only works with version 2 and 3 status types) (default: false) [$OPENVPN_EXPORTER_ALLOW_DUPLICATE_CN]
--enable-golang-metrics Enables golang and process metrics for the exporter) (default: false) [$OPENVPN_EXPORTER_ENABLE_GOLANG_METRICS]
--log.level value Only log messages with given severity (default: "info") [$OPENVPN_EXPORTER_LOG_LEVEL]
--help, -h Show help (default: false)
Expand Down
3 changes: 2 additions & 1 deletion example/version2.status
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ TITLE,OpenVPN 2.4.4 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKC
TIME,Thu Apr 30 13:55:44 2020,1588254944
HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Virtual IPv6 Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t),Username,Client ID,Peer ID
CLIENT_LIST,test@localhost,1.2.3.4:54190,10.80.0.65,,3860,3688,Thu Apr 30 13:55:38 2020,1588254938,test@localhost,0,0
CLIENT_LIST,test1@localhost,1.2.3.5:51053,10.68.0.25,,3871,3924,Thu Apr 30 13:55:40 2020,1588254940,test1@localhost,1,1
CLIENT_LIST,test@localhost,1.2.3.4:54190,10.80.0.65,,3860,3688,Thu Apr 30 13:55:38 2020,1588254938,test@localhost,1,1
CLIENT_LIST,test1@localhost,1.2.3.5:51053,10.68.0.25,,3871,3924,Thu Apr 30 13:55:40 2020,1588254940,test1@localhost,2,2
HEADER,ROUTING_TABLE,Virtual Address,Common Name,Real Address,Last Ref,Last Ref (time_t)
ROUTING_TABLE,10.80.0.65,test@localhost,1.2.3.4:54190,Thu Apr 30 13:55:40 2020,1588254940
ROUTING_TABLE,10.68.0.25,test1@localhost,1.2.3.5:51053,Thu Apr 30 13:55:42 2020,1588254942
Expand Down
3 changes: 2 additions & 1 deletion example/version3.status
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ TITLE OpenVPN 2.4.4 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKC
TIME Thu Apr 30 13:55:44 2020 1588254944
HEADER CLIENT_LIST Common Name Real Address Virtual Address Virtual IPv6 Address Bytes Received Bytes Sent Connected Since Connected Since (time_t) Username Client ID Peer ID
CLIENT_LIST test@localhost 1.2.3.4:54190 10.80.0.65 3860 3688 Thu Apr 30 13:55:38 2020 1588254938 test@localhost 0 0
CLIENT_LIST test1@localhost 1.2.3.5:51053 10.68.0.25 3871 3924 Thu Apr 30 13:55:40 2020 1588254940 test1@localhost 1 1
CLIENT_LIST test@localhost 1.2.3.4:54190 10.80.0.65 3860 3688 Thu Apr 30 13:55:38 2020 1588254938 test@localhost 1 1
CLIENT_LIST test1@localhost 1.2.3.5:51053 10.68.0.25 3871 3924 Thu Apr 30 13:55:40 2020 1588254940 test1@localhost 2 2
HEADER ROUTING_TABLE Virtual Address Common Name Real Address Last Ref Last Ref (time_t)
ROUTING_TABLE 10.80.0.65 test@localhost 1.2.3.4:54190 Thu Apr 30 13:55:40 2020 1588254940
ROUTING_TABLE 10.68.0.25 test1@localhost 1.2.3.5:51053 Thu Apr 30 13:55:42 2020 1588254942
Expand Down
48 changes: 34 additions & 14 deletions pkg/collector/openvpn.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/prometheus/client_golang/prometheus"
"strconv"

"github.com/patrickjahns/openvpn_exporter/pkg/openvpn"
)
Expand All @@ -12,6 +13,7 @@ import (
type OpenVPNCollector struct {
logger log.Logger
collectClientMetrics bool
allowDuplicateCn bool
OpenVPNServer []OpenVPNServer
LastUpdated *prometheus.Desc
ConnectedClients *prometheus.Desc
Expand All @@ -31,11 +33,12 @@ type OpenVPNServer struct {
}

// NewOpenVPNCollector returns a new OpenVPNCollector
func NewOpenVPNCollector(logger log.Logger, openVPNServer []OpenVPNServer, collectClientMetrics bool) *OpenVPNCollector {
func NewOpenVPNCollector(logger log.Logger, openVPNServer []OpenVPNServer, collectClientMetrics bool, allowDuplicateCn bool) *OpenVPNCollector {
return &OpenVPNCollector{
logger: logger,
OpenVPNServer: openVPNServer,
collectClientMetrics: collectClientMetrics,
allowDuplicateCn: allowDuplicateCn,

LastUpdated: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "last_updated"),
Expand All @@ -58,19 +61,19 @@ func NewOpenVPNCollector(logger log.Logger, openVPNServer []OpenVPNServer, colle
BytesReceived: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "bytes_received"),
"Amount of data received via the connection",
[]string{"server", "common_name"},
[]string{"server", "common_name", "unique_id"},
nil,
),
BytesSent: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "bytes_sent"),
"Amount of data sent via the connection",
[]string{"server", "common_name"},
[]string{"server", "common_name", "unique_id"},
nil,
),
ConnectedSince: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "connected_since"),
"Unixtimestamp when the connection was established",
[]string{"server", "common_name"},
[]string{"server", "common_name", "unique_id"},
nil,
),
ServerInfo: prometheus.NewDesc(
Expand Down Expand Up @@ -127,6 +130,7 @@ func (c *OpenVPNCollector) collect(ovpn OpenVPNServer, ch chan<- prometheus.Metr
}

connectedClients := 0
hasFacedZeroPeerId := false
var clientCommonNames []string
for _, client := range status.ClientList {
connectedClients++
Expand All @@ -141,30 +145,46 @@ func (c *OpenVPNCollector) collect(ovpn OpenVPNServer, ch chan<- prometheus.Metr
continue
}
if contains(clientCommonNames, client.CommonName) {
level.Warn(c.logger).Log(
"msg", "duplicate client common name in statusfile - duplicate metric dropped",
"commonName", client.CommonName,
)
continue
if !c.allowDuplicateCn {
level.Warn(c.logger).Log(
"msg", "duplicate client common name in statusfile - duplicate metric dropped (use --allow-duplicate-cn flag)",
"commonName", client.CommonName,
)
continue
}
if c.allowDuplicateCn && client.PeerID == -1 {
level.Warn(c.logger).Log(
"msg", "allow-duplicate-cn flag with a version 1 statusfile - duplicate metric dropped (use version 2 or 3)",
"commonName", client.CommonName,
)
continue
}
}
clientCommonNames = append(clientCommonNames, client.CommonName)
uniqueId := client.PeerID
if uniqueId == 0 { // In TCP mode, PeerID is always 0
if hasFacedZeroPeerId { // Use ClientID only if a 0 PeerID is matched twice (maybe it's the first item and we are in UDP mode)
uniqueId = -client.ClientID - 1 // ClientID starts at 0; But it may be duplicated with another 0 PeerID
}
hasFacedZeroPeerId = true
}
ch <- prometheus.MustNewConstMetric(
c.BytesReceived,
prometheus.GaugeValue,
prometheus.CounterValue,
client.BytesReceived,
ovpn.Name, client.CommonName,
ovpn.Name, client.CommonName, strconv.FormatInt(uniqueId, 10),
)
ch <- prometheus.MustNewConstMetric(
c.BytesSent,
prometheus.GaugeValue,
prometheus.CounterValue,
client.BytesSent,
ovpn.Name, client.CommonName,
ovpn.Name, client.CommonName, strconv.FormatInt(uniqueId, 10),
)
ch <- prometheus.MustNewConstMetric(
c.ConnectedSince,
prometheus.GaugeValue,
float64(client.ConnectedSince.Unix()),
ovpn.Name, client.CommonName,
ovpn.Name, client.CommonName, strconv.FormatInt(uniqueId, 10),
)
}
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ func Run() error {
Usage: "Disables per client (bytes_received, bytes_sent, connected_since) metrics",
EnvVars: []string{"OPENVPN_EXPORTER_DISABLE_CLIENT_METRICS"},
},
&cli.BoolFlag{
Name: "allow-duplicate-cn",
Usage: "Allow multiple connections with the same common name distinguished by the Peer ID (only works with version 2 and 3 status types)",
EnvVars: []string{"OPENVPN_EXPORTER_ALLOW_DUPLICATE_CN"},
Destination: &cfg.AllowDuplicateCN,
},
&cli.BoolFlag{
Name: "enable-golang-metrics",
Value: false,
Expand Down Expand Up @@ -145,6 +151,7 @@ func run(cfg *config.Config) error {
logger,
openVPServers,
cfg.StatusCollector.ExportClientMetrics,
cfg.AllowDuplicateCN,
))

http.Handle(cfg.Server.Path,
Expand Down
9 changes: 5 additions & 4 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ type Logs struct {

// Config defines the general configuration object
type Config struct {
Server Server
Logs Logs
StatusCollector StatusCollector
ExportGoMetrics bool
Server Server
Logs Logs
StatusCollector StatusCollector
ExportGoMetrics bool
AllowDuplicateCN bool
}

// StatusCollector contains configuration for the OpenVPN status collector
Expand Down
7 changes: 7 additions & 0 deletions pkg/openvpn/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type Client struct {
BytesReceived float64
BytesSent float64
ConnectedSince time.Time
ClientID int64
PeerID int64
}

// ServerInfo reflects information that was collected about the server
Expand Down Expand Up @@ -114,6 +116,7 @@ func parseStatusV1(reader io.Reader) (*Status, error) {
BytesReceived: bytesRec,
BytesSent: bytesSent,
ConnectedSince: parseTime(fields[4]),
PeerID: -1,
}
clients = append(clients, client)
}
Expand Down Expand Up @@ -142,12 +145,16 @@ func parseStatusV2AndV3(reader io.Reader, separator string) (*Status, error) {
bytesRec, _ := strconv.ParseFloat(fields[5], 64)
bytesSent, _ := strconv.ParseFloat(fields[6], 64)
connectedSinceInt, _ := strconv.ParseInt(fields[8], 10, 64)
clientIdInt, _ := strconv.ParseInt(fields[10], 10, 64)
peerIdInt, _ := strconv.ParseInt(fields[11], 10, 64)
client := Client{
CommonName: fields[1],
RealAddress: parseIP(fields[2]),
BytesReceived: bytesRec,
BytesSent: bytesSent,
ConnectedSince: time.Unix(connectedSinceInt, 0),
ClientID: clientIdInt,
PeerID: peerIdInt,
}
clients = append(clients, client)
} else if fields[0] == "GLOBAL_STATS" {
Expand Down