diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index b183a15..e622418 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -14,7 +14,7 @@ jobs: - name: Install Go uses: actions/setup-go@v2 with: - go-version: 1.16 + go-version: 1.17 - name: Set build info run: | echo "USER=$(whoami)" >> $GITHUB_ENV diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4864b98..1e1d9a0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,6 +13,7 @@ jobs: - 1.14.x - 1.15.x - 1.16.x + - 1.17.x - 1.x os: - ubuntu-latest @@ -24,14 +25,21 @@ jobs: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@v2 - - name: Start containers + - name: Start strongswan containers run: | for i in {1..10}; do echo $i: - docker-compose -f testdata/docker/docker-compose.yml exec -T moon /bin/sh -c 'ipsec statusall || true' | grep '64 bytes_i' && break || (docker-compose -f testdata/docker/docker-compose.yml up -d --force-recreate && sleep 30) + docker-compose -f testdata/docker/strongswan/docker-compose.yml exec -T moon /bin/sh -c 'ipsec statusall || true' | grep '64 bytes_i' && break || (docker-compose -f testdata/docker/strongswan/docker-compose.yml up -d --force-recreate && sleep 30) done - docker-compose -f testdata/docker/docker-compose.yml exec -T moon /bin/sh -c 'ipsec statusall || true' + docker-compose -f testdata/docker/strongswan/docker-compose.yml exec -T moon /bin/sh -c 'ipsec statusall || true' + - name: Start libreswan containers + run: | + for i in {1..10}; do + echo $i: + docker-compose -f testdata/docker/libreswan/docker-compose.yml exec -T moon /bin/sh -c 'ipsec status || true' | grep 'ESPin=84B' && break || (docker-compose -f testdata/docker/libreswan/docker-compose.yml up -d --force-recreate && sleep 30) + done + docker-compose -f testdata/docker/libreswan/docker-compose.yml exec -T moon /bin/sh -c 'ipsec status || true' - name: Test run: go test -coverprofile=coverage.txt -covermode=atomic ./... - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 diff --git a/README.md b/README.md index 59b4745..00b6835 100644 --- a/README.md +++ b/README.md @@ -14,31 +14,123 @@ make ./ipsec_exporter [flags] ``` -## Exported Metrics +## Exported metrics + +### Exported for both strongswan/libreswan | Metric | Meaning | Labels | --- | --- | --- | ipsec_up | Was the last scrape successful. | +| ipsec_ike_sas | Number of currently registered IKE SAs. | +| ipsec_half_open_ike_sas | Number of IKE SAs in half-open state. | +| ipsec_ike_sa_state | IKE SA state. | name, uid, version, local_host, local_id, remote_host, remote_id, remote_identity, vips +| ipsec_child_sa_state | Child SA state. | ike_sa_name, ike_sa_uid, ike_sa_version, ike_sa_local_host, ike_sa_local_id, ike_sa_remote_host, ike_sa_remote_id, ike_sa_remote_identity, ike_sa_vips, name, uid, reqid, mode, protocol, local_ts, remote_ts +| ipsec_child_sa_bytes_in | Number of input bytes processed. | ike_sa_name, ike_sa_uid, ike_sa_version, ike_sa_local_host, ike_sa_local_id, ike_sa_remote_host, ike_sa_remote_id, ike_sa_remote_identity, ike_sa_vips, name, uid, reqid, mode, protocol, local_ts, remote_ts +| ipsec_child_sa_bytes_out | Number of output bytes processed. | ike_sa_name, ike_sa_uid, ike_sa_version, ike_sa_local_host, ike_sa_local_id, ike_sa_remote_host, ike_sa_remote_id, ike_sa_remote_identity, ike_sa_vips, name, uid, reqid, mode, protocol, local_ts, remote_ts + +### Additionally exported for strongswan-only + +| Metric | Meaning | Labels +| --- | --- | --- | ipsec_uptime_seconds | Number of seconds since the daemon started. | | ipsec_workers_total | Number of worker threads. | | ipsec_idle_workers | Number of idle worker threads. | | ipsec_active_workers | Number of threads processing jobs. | | ipsec_queues | Number of queued jobs. | priority -| ipsec_ike_sas | Number of currently registered IKE SAs. | -| ipsec_half_open_ike_sas | Number of IKE SAs in half-open state. | | ipsec_pool_ips_total | Number of addresses in the pool. | name, address | ipsec_online_pool_ips | Number of leases online. | name, address | ipsec_offline_pool_ips | Number of leases offline. | name, address -| ipsec_ike_sa_state | IKE SA state. Created: 0, connecting: 1, established: 2, passive: 3, rekeying: 4, rekeyed: 5, deleting: 6, destroying: 7. | name, uid, version, local_host, local_id, remote_host, remote_id, remote_identity, vips | ipsec_ike_sa_established_seconds | Number of seconds since the IKE SA has been established. | name, uid, version, local_host, local_id, remote_host, remote_id, remote_identity, vips -| ipsec_child_sa_state | Child SA state. Created: 0, routed: 1, installing: 2, installed: 3, updating: 4, rekeying: 5, rekeyed: 6, retrying: 7, deleting: 8, deleted: 9, destroying: 10. | ike_sa_name, ike_sa_uid, ike_sa_version, ike_sa_local_host, ike_sa_local_id, ike_sa_remote_host, ike_sa_remote_id, ike_sa_remote_identity, ike_sa_vips, name, uid, reqid, mode, protocol, local_ts, remote_ts -| ipsec_child_sa_bytes_in | Number of input bytes processed. | ike_sa_name, ike_sa_uid, ike_sa_version, ike_sa_local_host, ike_sa_local_id, ike_sa_remote_host, ike_sa_remote_id, ike_sa_remote_identity, ike_sa_vips, name, uid, reqid, mode, protocol, local_ts, remote_ts | ipsec_child_sa_packets_in | Number of input packets processed. | ike_sa_name, ike_sa_uid, ike_sa_version, ike_sa_local_host, ike_sa_local_id, ike_sa_remote_host, ike_sa_remote_id, ike_sa_remote_identity, ike_sa_vips, name, uid, reqid, mode, protocol, local_ts, remote_ts -| ipsec_child_sa_bytes_out | Number of output bytes processed. | ike_sa_name, ike_sa_uid, ike_sa_version, ike_sa_local_host, ike_sa_local_id, ike_sa_remote_host, ike_sa_remote_id, ike_sa_remote_identity, ike_sa_vips, name, uid, reqid, mode, protocol, local_ts, remote_ts | ipsec_child_sa_packets_out | Number of output packets processed. | ike_sa_name, ike_sa_uid, ike_sa_version, ike_sa_local_host, ike_sa_local_id, ike_sa_remote_host, ike_sa_remote_id, ike_sa_remote_identity, ike_sa_vips, name, uid, reqid, mode, protocol, local_ts, remote_ts | ipsec_child_sa_installed_seconds | Number of seconds since the child SA has been installed. | ike_sa_name, ike_sa_uid, ike_sa_version, ike_sa_local_host, ike_sa_local_id, ike_sa_remote_host, ike_sa_remote_id, ike_sa_remote_identity, ike_sa_vips, name, uid, reqid, mode, protocol, local_ts, remote_ts -### Flags +### strongswan state mapping + +#### IKE SA + +| Name | State value +| --- | --- +| CREATED | 0 +| CONNECTING | 1 +| ESTABLISHED | 2 +| PASSIVE | 3 +| REKEYING | 4 +| REKEYED | 5 +| DELETING | 6 +| DESTROYING | 7 + +#### Child SA + +| Name | State value +| --- | --- +| CREATED | 0 +| ROUTED | 1 +| INSTALLING | 2 +| INSTALLED | 3 +| UPDATING | 4 +| REKEYING | 5 +| REKEYED | 6 +| RETRYING | 7 +| DELETING | 8 +| DELETED | 9 +| DESTROYING | 10 + +### libreswan state mapping + +| Name | State value +| --- | --- +| STATE_MAIN_R0 | 0 +| STATE_MAIN_I1 | 1 +| STATE_MAIN_R1 | 2 +| STATE_MAIN_I2 | 3 +| STATE_MAIN_R2 | 4 +| STATE_MAIN_I3 | 5 +| STATE_MAIN_R3 | 6 +| STATE_MAIN_I4 | 7 +| STATE_AGGR_R0 | 8 +| STATE_AGGR_I1 | 9 +| STATE_AGGR_R1 | 10 +| STATE_AGGR_I2 | 11 +| STATE_AGGR_R2 | 12 +| STATE_QUICK_R0 | 13 +| STATE_QUICK_I1 | 14 +| STATE_QUICK_R1 | 15 +| STATE_QUICK_I2 | 16 +| STATE_QUICK_R2 | 17 +| STATE_INFO | 18 +| STATE_INFO_PROTECTED | 19 +| STATE_XAUTH_R0 | 20 +| STATE_XAUTH_R1 | 21 +| STATE_MODE_CFG_R0 | 22 +| STATE_MODE_CFG_R1 | 23 +| STATE_MODE_CFG_R2 | 24 +| STATE_MODE_CFG_I1 | 25 +| STATE_XAUTH_I0 | 26 +| STATE_XAUTH_I1 | 27 +| STATE_IKEv1_ROOF | 28 +| STATE_V2_PARENT_I0 | 29 +| STATE_V2_PARENT_I1 | 30 +| STATE_V2_PARENT_I2 | 31 +| STATE_V2_PARENT_R0 | 32 +| STATE_V2_PARENT_R1 | 33 +| STATE_V2_IKE_AUTH_CHILD_I0 | 34 +| STATE_V2_IKE_AUTH_CHILD_R0 | 35 +| STATE_V2_NEW_CHILD_I0 | 36 +| STATE_V2_NEW_CHILD_I1 | 37 +| STATE_V2_REKEY_IKE_I0 | 38 +| STATE_V2_REKEY_IKE_I1 | 39 +| STATE_V2_REKEY_CHILD_I0 | 40 +| STATE_V2_REKEY_CHILD_I1 | 41 +| STATE_V2_NEW_CHILD_R0 | 42 +| STATE_V2_REKEY_IKE_R0 | 43 +| STATE_V2_REKEY_CHILD_R0 | 44 +| STATE_V2_ESTABLISHED_IKE_SA | 45 +| STATE_V2_ESTABLISHED_CHILD_SA | 46 +| STATE_V2_IKE_SA_DELETE | 47 +| STATE_V2_CHILD_SA_DELETE | 48 + +## Flags ```bash ./ipsec_exporter --help @@ -47,7 +139,8 @@ make * __`vici.address`:__ VICI socket address. Example: `unix:///var/run/charon.vici` or `tcp://127.0.0.1:4502`. * __`vici.timeout`:__ VICI socket connect timeout. * __`collector`:__ Collector type to scrape metrics with. `vici` or `ipsec`. -* __`ipsec.command`:__ Command to scrape IPsec metrics when the collector is configured to an `ipsec` binary. +* __`ipsec.command`:__ Command to scrape IPsec metrics when the collector is configured to an `ipsec` binary. + To use with libreswan, set to `ipsec status`. * __`web.listen-address`:__ Address to listen on for web interface and telemetry. * __`web.telemetry-path`:__ Path under which to expose metrics. * __`log.level`:__ Logging level. `info` by default. diff --git a/exporter/exporter.go b/exporter/exporter.go index c8c41c3..64ed954 100644 --- a/exporter/exporter.go +++ b/exporter/exporter.go @@ -3,7 +3,7 @@ package exporter import ( "fmt" - "net" + "math" "net/url" "os/exec" "regexp" @@ -15,7 +15,6 @@ import ( "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" "github.com/prometheus/client_golang/prometheus" - "github.com/strongswan/govici/vici" ) // Collector types. @@ -26,25 +25,9 @@ const ( const namespace = "ipsec" -const ( - prefixStatus = "Status of IKE charon daemon" - prefixPools = "Virtual IP pools (size/online/offline):" - prefixSA = "Security Associations" -) - var ( - reUptime = regexp.MustCompile(`^ uptime: .+, since (.+)$`) - reStats = regexp.MustCompile(`^ worker threads: (\d+) of (\d+) idle, (\d+)/(\d+)/(\d+)/(\d+) working, job queue: (\d+)/(\d+)/(\d+)/(\d+), scheduled: (\d+)$`) - rePool = regexp.MustCompile(`^ (.+?): (\d+)/(\d+)/(\d+)$`) - reSAHeader = regexp.MustCompile(`^Security Associations \((\d+) up, (\d+) connecting\):$`) - reSAPrefix = regexp.MustCompile(`^\s*([^\[]+)\[(\d+)]: `) - reSAStatus = regexp.MustCompile(`^([^ ]+) .+ ago, ([^\[]+)\[([^]]+)]\.\.\.([^\[]+)\[([^]]+)]$`) - reSAVersion = regexp.MustCompile(`^(.+) SPIs:`) - reSARemoteIdentity = regexp.MustCompile(`^Remote (.+) identity: (.+)$`) - reChildSAPrefix = regexp.MustCompile(`^\s*([^{]+){(\d+)}: `) - reChildSAStatus = regexp.MustCompile(`^([^,]+), ([^,]+), reqid (\d+), (.+) SPIs:.+`) - reChildSATraffic = regexp.MustCompile(`(\d+) bytes_i(?: \((\d+) pkts?[^)]*\))?, (\d+) bytes_o(?: \((\d+) pkts?[^)]*\))?`) - reChildSATS = regexp.MustCompile(`^ (.+) === (.+)$`) + reSSMarker = regexp.MustCompile(`(?m)` + ssSAHeaderRE.String()) + reLSMarker = regexp.MustCompile(`(?m)` + lsPrefix + `Connection list:$`) ) var ( @@ -79,32 +62,14 @@ var ( } ) var ( - ikeSAStates = map[string]float64{ - "CREATED": 0, - "CONNECTING": 1, - "ESTABLISHED": 2, - "PASSIVE": 3, - "REKEYING": 4, - "REKEYED": 5, - "DELETING": 6, - "DESTROYING": 7, - } - childSAStates = map[string]float64{ - "CREATED": 0, - "ROUTED": 1, - "INSTALLING": 2, - "INSTALLED": 3, - "UPDATING": 4, - "REKEYING": 5, - "REKEYED": 6, - "RETRYING": 7, - "DELETING": 8, - "DELETED": 9, - "DESTROYING": 10, - } + ikeSAStates = make(map[string]float64) + childSAStates = make(map[string]float64) ) -var now = time.Now +var ( + now = time.Now + tz = time.Local +) // Exporter collects IPsec stats via a VICI protocol or an ipsec binary // and exports them using the prometheus metrics package. @@ -173,77 +138,6 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) { e.collect(m, ch) } -func (e *Exporter) scrapeVICI() (m metrics, ok bool) { - e.mu.Lock() - defer e.mu.Unlock() - network, addr := e.address.Scheme, e.address.Host - if network == "unix" { - addr = e.address.Path - } - sess, err := vici.NewSession(vici.WithAddr(network, addr), vici.WithDialContext((&net.Dialer{Timeout: e.timeout}).DialContext)) - if err != nil { - level.Error(e.logger).Log("msg", "Failed to connect to charon", "err", err) - return - } - defer sess.Close() - - msg, err := sess.CommandRequest("stats", nil) - if err != nil { - level.Error(e.logger).Log("msg", "Failed to send command", "cmd", "stats", "err", err) - return - } - if msg.Err() != nil { - level.Error(e.logger).Log("msg", "Failed to process command response", "cmd", "stats", "err", err) - return - } - if err = vici.UnmarshalMessage(msg, &m.Stats); err != nil { - level.Error(e.logger).Log("msg", "Failed to unmarshal command response", "cmd", "stats", "err", err) - return - } - - msg, err = sess.CommandRequest("get-pools", nil) - if err != nil { - level.Error(e.logger).Log("msg", "Failed to send command", "cmd", "get-pools", "err", err) - return - } - if msg.Err() != nil { - level.Error(e.logger).Log("msg", "Failed to process command response", "cmd", "get-pools", "err", err) - return - } - pools := make(map[string]pool) - if err = vici.UnmarshalMessage(msg, pools); err != nil { - level.Error(e.logger).Log("msg", "Failed to unmarshal command response", "cmd", "get-pools", "err", err) - return - } - for name, pool := range pools { - pool.Name = name - m.Pools = append(m.Pools, pool) - } - - stream, err := sess.StreamedCommandRequest("list-sas", "list-sa", nil) - if err != nil { - level.Error(e.logger).Log("msg", "Failed to send command", "cmd", "list-sas", "err", err) - return - } - for _, msg := range stream.Messages() { - if msg.Err() != nil { - level.Error(e.logger).Log("msg", "Failed to process command response", "cmd", "list-sas", "err", err) - return - } - ikeSAs := make(map[string]ikeSA) - if err = vici.UnmarshalMessage(msg, ikeSAs); err != nil { - level.Error(e.logger).Log("msg", "Failed to unmarshal command response", "cmd", "list-sas", "err", err) - return - } - for name, ikeSA := range ikeSAs { - ikeSA.Name = name - m.IKESAs = append(m.IKESAs, &ikeSA) - } - } - ok = true - return -} - func (e *Exporter) scrapeIpsec() (m metrics, ok bool) { e.mu.Lock() defer e.mu.Unlock() @@ -253,189 +147,39 @@ func (e *Exporter) scrapeIpsec() (m metrics, ok bool) { level.Error(e.logger).Log("msg", "Failed to execute command", "cmd", cmd, "output", output, "err", err) return } - // Looking for prefixes then scanning lines below for matching regexps. - // Using an additional newline just to avoid processing SAs not added yet - lines := strings.Split(string(output)+"\n", "\n") - for i, line := range lines { - switch { - case strings.HasPrefix(line, prefixStatus): - j := i - if i+1 < len(lines) { - j++ - } - for _, line := range lines[j:] { - if !strings.HasPrefix(line, " ") { - break - } - matches := reUptime.FindStringSubmatch(line) - if matches != nil { - m.Stats.Uptime.Since = matches[1] - continue - } - matches = reStats.FindStringSubmatch(line) - if matches != nil { - m.Stats.Workers.Idle, _ = strconv.ParseUint(matches[1], 10, 64) - m.Stats.Workers.Total, _ = strconv.ParseUint(matches[2], 10, 64) - m.Stats.Workers.Active.Critical, _ = strconv.ParseUint(matches[3], 10, 64) - m.Stats.Workers.Active.High, _ = strconv.ParseUint(matches[4], 10, 64) - m.Stats.Workers.Active.Medium, _ = strconv.ParseUint(matches[5], 10, 64) - m.Stats.Workers.Active.Low, _ = strconv.ParseUint(matches[6], 10, 64) - m.Stats.Queues.Critical, _ = strconv.ParseUint(matches[7], 10, 64) - m.Stats.Queues.High, _ = strconv.ParseUint(matches[8], 10, 64) - m.Stats.Queues.Medium, _ = strconv.ParseUint(matches[9], 10, 64) - m.Stats.Queues.Low, _ = strconv.ParseUint(matches[10], 10, 64) - m.Stats.Scheduled, _ = strconv.ParseUint(matches[11], 10, 64) - } - } - case line == prefixPools: - j := i - if i+1 < len(lines) { - j++ - } - for _, line := range lines[j:] { - matches := rePool.FindStringSubmatch(line) - if matches == nil { - break - } - pool := pool{Address: matches[1]} - pool.Size, _ = strconv.ParseUint(matches[2], 10, 64) - pool.Online, _ = strconv.ParseUint(matches[3], 10, 64) - pool.Offline, _ = strconv.ParseUint(matches[4], 10, 64) - m.Pools = append(m.Pools, pool) - } - case strings.HasPrefix(line, prefixSA): - matches := reSAHeader.FindStringSubmatch(line) - if matches != nil { - m.Stats.IKESAs.Total, _ = strconv.ParseUint(matches[1], 10, 64) - m.Stats.IKESAs.HalfOpen, _ = strconv.ParseUint(matches[2], 10, 64) - j := i - if i+1 < len(lines) { - j++ - } - var ( - prefix, childPrefix []string - sa, prevSA *ikeSA - childSA2 *childSA - ) - - Loop: - for _, line := range lines[j:] { - if (prefix != nil && !strings.HasPrefix(line, prefix[0])) || (childPrefix != nil && !strings.HasPrefix(line, childPrefix[0])) { - if sa != nil { - prevSA = sa - } - prefix, childPrefix, sa, childSA2 = nil, nil, nil, nil - } - if prefix == nil && childPrefix == nil { - matches = reSAPrefix.FindStringSubmatch(line) - if matches != nil { - prefix = matches - sa = &ikeSA{ - Name: matches[1], - ChildSAs: make(map[string]*childSA), - } - n, _ := strconv.ParseUint(matches[2], 10, 32) - sa.UID = uint32(n) - m.IKESAs = append(m.IKESAs, sa) - - } else { - matches = reChildSAPrefix.FindStringSubmatch(line) - if matches != nil { - childPrefix = matches - childSA2 = &childSA{Name: matches[1]} - n, _ := strconv.ParseUint(matches[2], 10, 32) - childSA2.UID = uint32(n) - if prevSA != nil { - prevSA.ChildSAs[fmt.Sprintf("%s-%d", childSA2.Name, childSA2.UID)] = childSA2 - } - } - } - } - switch { - case prefix != nil: - line = strings.TrimPrefix(line, prefix[0]) - matches = reSAStatus.FindStringSubmatch(line) - if matches != nil { - sa.State = matches[1] - sa.LocalHost = matches[2] - sa.LocalID = matches[3] - sa.RemoteHost = matches[4] - sa.RemoteID = matches[5] - continue - } - matches = reSAVersion.FindStringSubmatch(line) - if matches != nil { - switch matches[1] { - case "IKEv1": - sa.Version = 1 - case "IKEv2": - sa.Version = 2 - } - continue - } - matches = reSARemoteIdentity.FindStringSubmatch(line) - if matches != nil { - if matches[1] == "XAuth" { - sa.RemoteXAuthID = matches[2] - } else { - sa.RemoteEAPID = matches[2] - } - } - case childPrefix != nil: - line = strings.TrimPrefix(line, childPrefix[0]) - matches = reChildSAStatus.FindStringSubmatch(line) - if matches != nil { - childSA2.State = matches[1] - childSA2.Mode = matches[2] - n, _ := strconv.ParseUint(matches[3], 10, 64) - childSA2.ReqID = uint32(n) - childSA2.Protocol = matches[4] - continue - } - matches = reChildSATraffic.FindStringSubmatch(line) - if matches != nil { - childSA2.InBytes, _ = strconv.ParseUint(matches[1], 10, 64) - childSA2.OutBytes, _ = strconv.ParseUint(matches[3], 10, 64) - if matches[2] != "" && matches[4] != "" { - childSA2.InPackets, _ = strconv.ParseUint(matches[2], 10, 64) - childSA2.OutPackets, _ = strconv.ParseUint(matches[4], 10, 64) - } - continue - } - matches = reChildSATS.FindStringSubmatch(line) - if matches != nil { - childSA2.LocalTS = strings.Split(matches[1], " ") - childSA2.RemoteTS = strings.Split(matches[2], " ") - } - default: - break Loop - } - } - } - } + switch { + case reSSMarker.Match(output): + level.Debug(e.logger).Log("msg", "Output type is detected as strongswan", "cmd", cmd) + return e.scrapeStrongswan(output) + case reLSMarker.Match(output): + level.Debug(e.logger).Log("msg", "Output type is detected as libreswan", "cmd", cmd) + return e.scrapeLibreswan(output) } - ok = true + level.Error(e.logger).Log("msg", "Failed to recognize output type", "cmd", cmd, "output", output) return } func (e *Exporter) collect(m metrics, ch chan<- prometheus.Metric) { - ch <- prometheus.MustNewConstMetric(e.up, prometheus.GaugeValue, 1) if m.Stats.Uptime.Since != "" { - uptime, err := time.ParseInLocation("Jan _2 15:04:05 2006", m.Stats.Uptime.Since, time.Local) + uptime, err := time.ParseInLocation("Jan _2 15:04:05 2006", m.Stats.Uptime.Since, tz) if err != nil { ch <- prometheus.MustNewConstMetric(e.up, prometheus.GaugeValue, 0) - level.Error(e.logger).Log("msg", "Failed to unmarshal uptime", "uptime", m.Stats.Uptime.Since, "err", err) + level.Error(e.logger).Log("msg", "Failed to parse uptime", "uptime", m.Stats.Uptime.Since, "err", err) return } ch <- prometheus.MustNewConstMetric(e.uptime, prometheus.GaugeValue, now().Round(time.Second).Sub(uptime).Seconds()) } - ch <- prometheus.MustNewConstMetric(e.workers, prometheus.GaugeValue, float64(m.Stats.Workers.Total)) - ch <- prometheus.MustNewConstMetric(e.idleWorkers, prometheus.GaugeValue, float64(m.Stats.Workers.Idle)) - ch <- prometheus.MustNewConstMetric(e.activeWorkers, prometheus.GaugeValue, float64(m.Stats.Workers.Active.Total())) - ch <- prometheus.MustNewConstMetric(e.queues, prometheus.GaugeValue, float64(m.Stats.Queues.Critical), "critical") - ch <- prometheus.MustNewConstMetric(e.queues, prometheus.GaugeValue, float64(m.Stats.Queues.High), "high") - ch <- prometheus.MustNewConstMetric(e.queues, prometheus.GaugeValue, float64(m.Stats.Queues.Medium), "medium") - ch <- prometheus.MustNewConstMetric(e.queues, prometheus.GaugeValue, float64(m.Stats.Queues.Low), "low") + if m.Stats.Workers != nil { + ch <- prometheus.MustNewConstMetric(e.workers, prometheus.GaugeValue, float64(m.Stats.Workers.Total)) + ch <- prometheus.MustNewConstMetric(e.idleWorkers, prometheus.GaugeValue, float64(m.Stats.Workers.Idle)) + ch <- prometheus.MustNewConstMetric(e.activeWorkers, prometheus.GaugeValue, float64(m.Stats.Workers.Active.Total())) + } + if m.Stats.Queues != nil { + ch <- prometheus.MustNewConstMetric(e.queues, prometheus.GaugeValue, float64(m.Stats.Queues.Critical), "critical") + ch <- prometheus.MustNewConstMetric(e.queues, prometheus.GaugeValue, float64(m.Stats.Queues.High), "high") + ch <- prometheus.MustNewConstMetric(e.queues, prometheus.GaugeValue, float64(m.Stats.Queues.Medium), "medium") + ch <- prometheus.MustNewConstMetric(e.queues, prometheus.GaugeValue, float64(m.Stats.Queues.Low), "low") + } ch <- prometheus.MustNewConstMetric(e.ikeSAs, prometheus.GaugeValue, float64(m.Stats.IKESAs.Total)) ch <- prometheus.MustNewConstMetric(e.halfOpenIKESAs, prometheus.GaugeValue, float64(m.Stats.IKESAs.HalfOpen)) for _, pool := range m.Pools { @@ -455,30 +199,51 @@ func (e *Exporter) collect(m metrics, ch chan<- prometheus.Metric) { ikeSA.RemoteXAuthID + ikeSA.RemoteEAPID, strings.Join(append(ikeSA.LocalVIPs, ikeSA.RemoteVIPs...), ", "), } - ch <- prometheus.MustNewConstMetric(e.ikeSAState, prometheus.GaugeValue, ikeSAStates[ikeSA.State], labelValues...) + state := math.NaN() + if f, ok := ikeSAStates[ikeSA.State]; ok { + state = f + } + if !math.IsNaN(state) { + ch <- prometheus.MustNewConstMetric(e.ikeSAState, prometheus.GaugeValue, state, labelValues...) + } if ikeSA.State == "ESTABLISHED" && ikeSA.Established != nil { ch <- prometheus.MustNewConstMetric(e.establishedIKESA, prometheus.GaugeValue, float64(*ikeSA.Established), labelValues...) } for _, childSA := range ikeSA.ChildSAs { + reqID := "" + if childSA.ReqID != nil { + reqID = strconv.FormatUint(uint64(*childSA.ReqID), 10) + } childLabelValues := append(labelValues, []string{ childSA.Name, strconv.FormatUint(uint64(childSA.UID), 10), childSA.Mode, childSA.Protocol, - strconv.FormatUint(uint64(childSA.ReqID), 10), + reqID, strings.Join(childSA.LocalTS, ", "), strings.Join(childSA.RemoteTS, ", "), }...) - ch <- prometheus.MustNewConstMetric(e.childSAState, prometheus.GaugeValue, childSAStates[childSA.State], childLabelValues...) + state := math.NaN() + if f, ok := childSAStates[childSA.State]; ok { + state = f + } + if !math.IsNaN(state) { + ch <- prometheus.MustNewConstMetric(e.childSAState, prometheus.GaugeValue, state, childLabelValues...) + } ch <- prometheus.MustNewConstMetric(e.childSABytesIn, prometheus.GaugeValue, float64(childSA.InBytes), childLabelValues...) - ch <- prometheus.MustNewConstMetric(e.childSAPacketsIn, prometheus.GaugeValue, float64(childSA.InPackets), childLabelValues...) + if childSA.InPackets != nil { + ch <- prometheus.MustNewConstMetric(e.childSAPacketsIn, prometheus.GaugeValue, float64(*childSA.InPackets), childLabelValues...) + } ch <- prometheus.MustNewConstMetric(e.childSABytesOut, prometheus.GaugeValue, float64(childSA.OutBytes), childLabelValues...) - ch <- prometheus.MustNewConstMetric(e.childSAPacketsOut, prometheus.GaugeValue, float64(childSA.OutPackets), childLabelValues...) + if childSA.OutPackets != nil { + ch <- prometheus.MustNewConstMetric(e.childSAPacketsOut, prometheus.GaugeValue, float64(*childSA.OutPackets), childLabelValues...) + } if childSA.Installed != nil { ch <- prometheus.MustNewConstMetric(e.childSAInstalled, prometheus.GaugeValue, float64(*childSA.Installed), childLabelValues...) } } } + ch <- prometheus.MustNewConstMetric(e.up, prometheus.GaugeValue, 1) } // New returns an initialized exporter. diff --git a/exporter/exporter_test.go b/exporter/exporter_test.go index d4db252..e9edd9e 100644 --- a/exporter/exporter_test.go +++ b/exporter/exporter_test.go @@ -1,10 +1,8 @@ package exporter import ( - "bytes" - "io/ioutil" - "net/url" "os" + "strings" "testing" "time" @@ -13,11 +11,14 @@ import ( "github.com/prometheus/client_golang/prometheus/testutil" ) -func TestExporter(t *testing.T) { - now = func() time.Time { - return time.Unix(0, 0) - } - exporter, err := New(CollectorVICI, nil, time.Second, nil, log.NewNopLogger()) +func TestMain(m *testing.M) { + now = func() time.Time { return time.Unix(0, 0).UTC() } + tz = time.UTC + os.Exit(m.Run()) +} + +func TestExporter_Collect(t *testing.T) { + exporter, err := New(CollectorIpsec, nil, 0, nil, log.NewNopLogger()) if err != nil { t.Fatalf("New() = _, %v; want nil", err) } @@ -28,7 +29,7 @@ func TestExporter(t *testing.T) { Uptime: uptime{ Since: now().Round(time.Second).Add(-3 * time.Minute).Format("Jan _2 15:04:05 2006"), }, - Workers: workers{ + Workers: &workers{ Total: 10, Idle: 5, Active: queues{ @@ -38,13 +39,13 @@ func TestExporter(t *testing.T) { Low: 4, }, }, - Queues: queues{ + Queues: &queues{ Critical: 1, High: 2, Medium: 3, Low: 4, }, - Scheduled: 12, + Scheduled: newUint64(12), IKESAs: ikeSAs{ Total: 10, HalfOpen: 5, @@ -83,28 +84,28 @@ func TestExporter(t *testing.T) { "named-3": { Name: "named", UID: 3, - ReqID: 4, + ReqID: newUint32(4), State: "INSTALLED", Mode: "TUNNEL", Protocol: "AH", InBytes: 123, - InPackets: 456, + InPackets: newUint64(456), OutBytes: 789, - OutPackets: 901, + OutPackets: newUint64(901), LocalTS: []string{"192.168.0.0/24", "192.168.1.0/24"}, RemoteTS: []string{"192.168.2.0/24", "192.168.3.0/24"}, }, "named-4": { Name: "named", UID: 4, - ReqID: 5, + ReqID: newUint32(5), State: "INSTALLED", Mode: "TUNNEL", Protocol: "AH", InBytes: 124, - InPackets: 457, + InPackets: newUint64(457), OutBytes: 790, - OutPackets: 902, + OutPackets: newUint64(902), Installed: &sec, LocalTS: []string{"192.168.0.0/24", "192.168.1.0/24"}, RemoteTS: []string{"192.168.2.0/24", "192.168.3.0/24"}, @@ -124,14 +125,14 @@ func TestExporter(t *testing.T) { "named-5": { Name: "named", UID: 5, - ReqID: 6, + ReqID: newUint32(6), State: "INSTALLED", Mode: "TUNNEL", Protocol: "AH", InBytes: 125, - InPackets: 458, + InPackets: newUint64(458), OutBytes: 791, - OutPackets: 903, + OutPackets: newUint64(903), LocalTS: []string{"192.168.0.0/24", "192.168.1.0/24"}, RemoteTS: []string{"192.168.2.0/24", "192.168.3.0/24"}, }, @@ -140,7 +141,7 @@ func TestExporter(t *testing.T) { }, }, true } - f, err := os.Open("testdata/metrics-1.txt") + f, err := os.Open("testdata/metrics.txt") if err != nil { t.Fatalf("os.Open() = _, %v; want nil", err) } @@ -150,49 +151,24 @@ func TestExporter(t *testing.T) { } } -func TestExporter_Integration(t *testing.T) { +func TestExporter_Collect_Unknown(t *testing.T) { if testing.Short() { - t.Skip("skipping TestExporter_Integration during short test") - } - tests := []struct { - Name string - CollectorType int - }{ - { - Name: "VICI", - CollectorType: CollectorVICI, - }, - { - Name: "ipsec", - CollectorType: CollectorIpsec, - }, + t.Skip("skipping TestExporter_Collect_Unknown during short test") } - address, _ := url.Parse("tcp://127.0.0.1:4502") - cmd, _ := shlex.Split("docker-compose -f ../testdata/docker/docker-compose.yml exec -T moon /bin/sh -c 'ipsec statusall || true'") - b, err := ioutil.ReadFile("testdata/metrics-2.txt") + cmd, _ := shlex.Split("docker-compose -f ../testdata/docker/libreswan/docker-compose.yml exec -T moon /bin/ls") + exporter, err := New(CollectorIpsec, nil, time.Second, cmd, log.NewNopLogger()) if err != nil { - panic("failed to read testdata/metrics-2.txt: " + err.Error()) - } - metricNames := []string{ - "ipsec_child_sa_bytes_in", - "ipsec_child_sa_bytes_out", - "ipsec_child_sa_packets_in", - "ipsec_child_sa_packets_out", - "ipsec_child_sa_state", - "ipsec_half_open_ike_sas", - "ipsec_ike_sa_state", - "ipsec_ike_sas", - "ipsec_up", + t.Fatalf("New() = _, %v; want nil", err) } - for _, td := range tests { - t.Run(td.Name, func(t *testing.T) { - exporter, err := New(td.CollectorType, address, time.Second, cmd, log.NewNopLogger()) - if err != nil { - t.Fatalf("New() = _, %v; want nil", err) - } - if err := testutil.CollectAndCompare(exporter, bytes.NewReader(b), metricNames...); err != nil { - t.Errorf("testutil.CollectAndCompare() = %v; want nil", err) - } - }) + expected := ` +# HELP ipsec_up Was the last scrape successful. +# TYPE ipsec_up gauge +ipsec_up 0 +` + if err := testutil.CollectAndCompare(exporter, strings.NewReader(expected)); err != nil { + t.Errorf("testutil.CollectAndCompare() = %v; want nil", err) } } + +func newUint32(n uint32) *uint32 { return &n } +func newUint64(n uint64) *uint64 { return &n } diff --git a/exporter/helpers_test.go b/exporter/helpers_test.go new file mode 100644 index 0000000..d623a01 --- /dev/null +++ b/exporter/helpers_test.go @@ -0,0 +1,69 @@ +package exporter + +import ( + "bytes" + "testing" + + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "github.com/prometheus/common/expfmt" +) + +func collect(t *testing.T, c prometheus.Collector) []byte { + reg := prometheus.NewPedanticRegistry() + if err := reg.Register(c); err != nil { + t.Fatalf("failed to register exporter: %v", err) + } + got, err := reg.Gather() + if err != nil { + t.Fatalf("failed to gather metrics: %v", err) + } + var buf bytes.Buffer + enc := expfmt.NewEncoder(&buf, expfmt.FmtText) + for _, mf := range got { + if err := enc.Encode(mf); err != nil { + t.Fatalf("failed to encode metric: %v", err) + } + } + return buf.Bytes() +} + +var redactedLbls = []string{ + "uid", + "ike_sa_uid", + "reqid", +} + +type redactedMetric struct { + prometheus.Metric +} + +func (m redactedMetric) Write(out *dto.Metric) error { + if err := m.Metric.Write(out); err != nil { + return err + } + for _, lbl := range out.Label { + for _, name := range redactedLbls { + if lbl.Name != nil && lbl.Value != nil && *lbl.Name == name && *lbl.Value != "" { + *lbl.Value = "X" + break + } + } + } + return nil +} + +type redactor struct { + prometheus.Collector +} + +func (r redactor) Collect(ch chan<- prometheus.Metric) { + buf := make(chan prometheus.Metric) + go func() { + r.Collector.Collect(buf) + close(buf) + }() + for m := range buf { + ch <- redactedMetric{m} + } +} diff --git a/exporter/libreswan.go b/exporter/libreswan.go new file mode 100644 index 0000000..797f09d --- /dev/null +++ b/exporter/libreswan.go @@ -0,0 +1,264 @@ +package exporter + +import ( + "fmt" + "regexp" + "strconv" + "strings" +) + +const ( + lsPrefix = `^[^ ]+ ` + lsIPAddrPart = `[a-f0-9:.]+` + lsIPNetPart = lsIPAddrPart + `/\d+` + lsConnPart = `"(?P[^"]+)"(?P\[\d+])?` + lsConn = `(?P` + + lsPrefix + + lsConnPart + + `)` + + `:[ ]+` + lsAddr = `(?P` + lsIPNetPart + `===)?` + + `(?P` + lsIPAddrPart + `)` + + `(?P<[^>]+?>)?` + + `(?P\[[^\]]+?])?` + + `(?P---` + lsIPAddrPart + `)?` + + `\.\.\.` + + `(?P` + lsIPAddrPart + `---)?` + + `(?P` + lsIPAddrPart + `|%any)` + + `(?P<[^>]+>)?` + + `(?P\[[^\]]+])?` + + `(?P===` + lsIPNetPart + `)?;` + lsState = `(?P` + + lsPrefix + + `#(?P\d+): ` + + lsConnPart + + `)` + + `(:\d+(\(tcp\))?)?` + + `(` + lsIPAddrPart + `)?(:[^ ]+)? ` +) + +var lsStates = map[string]float64{ + "STATE_MAIN_R0": 0, + "STATE_MAIN_I1": 1, + "STATE_MAIN_R1": 2, + "STATE_MAIN_I2": 3, + "STATE_MAIN_R2": 4, + "STATE_MAIN_I3": 5, + "STATE_MAIN_R3": 6, + "STATE_MAIN_I4": 7, + "STATE_AGGR_R0": 8, + "STATE_AGGR_I1": 9, + "STATE_AGGR_R1": 10, + "STATE_AGGR_I2": 11, + "STATE_AGGR_R2": 12, + "STATE_QUICK_R0": 13, + "STATE_QUICK_I1": 14, + "STATE_QUICK_R1": 15, + "STATE_QUICK_I2": 16, + "STATE_QUICK_R2": 17, + "STATE_INFO": 18, + "STATE_INFO_PROTECTED": 19, + "STATE_XAUTH_R0": 20, + "STATE_XAUTH_R1": 21, + "STATE_MODE_CFG_R0": 22, + "STATE_MODE_CFG_R1": 23, + "STATE_MODE_CFG_R2": 24, + "STATE_MODE_CFG_I1": 25, + "STATE_XAUTH_I0": 26, + "STATE_XAUTH_I1": 27, + "STATE_IKEv1_ROOF": 28, + + "STATE_V2_PARENT_I0": 29, + "STATE_V2_PARENT_I1": 30, + "STATE_V2_PARENT_I2": 31, + "STATE_V2_PARENT_R0": 32, + "STATE_V2_PARENT_R1": 33, + "STATE_V2_IKE_AUTH_CHILD_I0": 34, + "STATE_V2_IKE_AUTH_CHILD_R0": 35, + "STATE_V2_NEW_CHILD_I0": 36, + "STATE_V2_NEW_CHILD_I1": 37, + "STATE_V2_REKEY_IKE_I0": 38, + "STATE_V2_REKEY_IKE_I1": 39, + "STATE_V2_REKEY_CHILD_I0": 40, + "STATE_V2_REKEY_CHILD_I1": 41, + "STATE_V2_NEW_CHILD_R0": 42, + "STATE_V2_REKEY_IKE_R0": 43, + "STATE_V2_REKEY_CHILD_R0": 44, + "STATE_V2_ESTABLISHED_IKE_SA": 45, + "STATE_V2_ESTABLISHED_CHILD_SA": 46, + "STATE_V2_IKE_SA_DELETE": 47, + "STATE_V2_CHILD_SA_DELETE": 48, +} + +var ( + lsConnRE = regexp.MustCompile(lsConn) + lsAddrRE = regexp.MustCompile(lsAddr) +) + +var ( + lsStateRE = regexp.MustCompile(lsState) + lsParentIDRE = regexp.MustCompile(`; isakmp#(\d+)`) + lsStateNameRE = regexp.MustCompile(`(STATE_\w+)`) + lsSPIRE = regexp.MustCompile(`([a-z]+)[?:.][a-f0-9]+@` + lsIPAddrPart) + lsTrafficRE = regexp.MustCompile(`(AHin|AHout|ESPin|ESPout|IPCOMPin|IPCOMPout)=(\d+)(B|KB|MB)`) + lsUsernameRE = regexp.MustCompile(` username=(.+)$`) +) + +var lsStatsRE = regexp.MustCompile(`IKE SAs: total\((\d+)\), half-open\((\d+)\)`) + +func (e *Exporter) scrapeLibreswan(b []byte) (m metrics, ok bool) { + ikeSAs := make(map[string]*ikeSA) + childSAs := make(map[string]*childSA) + localTS := make(map[string]string) + remoteTS := make(map[string]string) + lines := strings.Split(string(b)+"\n", "\n") + for i := 0; i < len(lines); i++ { + if matches := findNamedSubmatch(lsConnRE, lines[i]); matches != nil { + name := matches["conname"] + matches["coninst"] + s := strings.TrimPrefix(lines[i], matches["prefix"]) + if m := findNamedSubmatch(lsAddrRE, s); m != nil { + localTS[name] = strings.Trim(m["leftclient"], "=") + remoteTS[name] = strings.Trim(m["rightclient"], "=") + localID := m["leftid"] + if localID != "" { + localID = strings.TrimPrefix(localID[1:len(localID)-1], "@") + } + remoteID := m["rightid"] + if remoteID != "" { + remoteID = strings.TrimPrefix(remoteID[1:len(remoteID)-1], "@") + } + ikeSAs[name] = &ikeSA{ + Name: name, + LocalHost: m["leftaddr"], + LocalID: localID, + RemoteHost: m["rightaddr"], + RemoteID: remoteID, + ChildSAs: make(map[string]*childSA), + } + } + } else if matches := findNamedSubmatch(lsStateRE, lines[i]); matches != nil { + name := matches["conname"] + matches["coninst"] + key := matches["prefix"] + n, _ := strconv.ParseUint(matches["serialno"], 10, 32) + child := false + if m := lsParentIDRE.FindStringSubmatch(lines[i]); m != nil { + child = true + childSAs[key] = &childSA{ + Name: name, + UID: uint32(n), + } + if s := localTS[name]; s != "" { + childSAs[key].LocalTS = append(childSAs[key].LocalTS, s) + } + if s := remoteTS[name]; s != "" { + childSAs[key].RemoteTS = append(childSAs[key].RemoteTS, s) + } + } + for ; i < len(lines); i++ { + if strings.HasPrefix(lines[i], matches["prefix"]) { + s := strings.TrimPrefix(lines[i], matches["prefix"]) + if child { + if ikeSA, ok := ikeSAs[name]; ok { + ikeSA.ChildSAs[fmt.Sprintf("%s-%d", childSAs[key].Name, childSAs[key].UID)] = childSAs[key] + } + if m := lsStateNameRE.FindStringSubmatch(s); m != nil { + childSAs[key].State = m[1] + if ikeSA, ok := ikeSAs[name]; ok { + if strings.HasPrefix(m[1], "STATE_V2_") { + ikeSA.Version = 2 + } else { + ikeSA.Version = 1 + } + } + } + for _, m := range lsSPIRE.FindAllStringSubmatch(s, -1) { + if m[1] == "tun" { + childSAs[key].Mode = "TUNNEL" + break + } + } + for _, m := range lsTrafficRE.FindAllStringSubmatch(s, -1) { + n, _ = strconv.ParseUint(m[2], 10, 64) + switch m[3] { + case "MB": + n *= 1024 + fallthrough + case "KB": + n *= 1024 + } + switch m[1] { + case "AHin", "AHout": + childSAs[key].Protocol = "AH" + case "ESPin", "ESPout": + childSAs[key].Protocol = "ESP" + case "IPCOMPin", "IPCOMPout": + childSAs[key].Protocol = "IPCOMP" + } + switch strings.TrimPrefix(m[1], childSAs[key].Protocol) { + case "in": + childSAs[key].InBytes = n + case "out": + childSAs[key].OutBytes = n + } + } + if m := lsUsernameRE.FindStringSubmatch(s); m != nil { + if ikeSA, ok := ikeSAs[name]; ok { + ikeSA.RemoteXAuthID = m[1] + } + } + } else { + if ikeSA, ok := ikeSAs[name]; ok { + ikeSA.UID = uint32(n) + } + if m := lsStateNameRE.FindStringSubmatch(s); m != nil { + if ikeSA, ok := ikeSAs[name]; ok { + ikeSA.State = m[1] + if strings.HasPrefix(m[1], "STATE_V2_") { + ikeSA.Version = 2 + } else { + ikeSA.Version = 1 + } + } + } + } + } else { + i-- + break + } + } + } else if matches := lsStatsRE.FindStringSubmatch(lines[i]); matches != nil { + n, _ := strconv.ParseUint(matches[1], 10, 64) + m.Stats.IKESAs.Total = n + n, _ = strconv.ParseUint(matches[2], 10, 64) + m.Stats.IKESAs.HalfOpen = n + } + } + for _, ikeSA := range ikeSAs { + if ikeSA.UID > 0 { + m.IKESAs = append(m.IKESAs, ikeSA) + } + } + ok = true + return +} + +func findNamedSubmatch(re *regexp.Regexp, s string) map[string]string { + m := re.FindStringSubmatch(s) + if m == nil { + return nil + } + result := make(map[string]string) + for i, name := range re.SubexpNames() { + if i > 0 && name != "" { + result[name] = m[i] + } + } + return result +} + +func init() { + for k, v := range lsStates { + ikeSAStates[k] = v + childSAStates[k] = v + } +} diff --git a/exporter/libreswan_test.go b/exporter/libreswan_test.go new file mode 100644 index 0000000..e9be0d0 --- /dev/null +++ b/exporter/libreswan_test.go @@ -0,0 +1,68 @@ +package exporter + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/go-kit/kit/log" + "github.com/google/shlex" + "github.com/prometheus/client_golang/prometheus/testutil" +) + +func TestExporter_scrapeLibreswan(t *testing.T) { + files, err := filepath.Glob("testdata/libreswan/*-command.txt") + if err != nil { + panic("failed to list test files: " + err.Error()) + } + for _, file := range files { + t.Run(file, func(t *testing.T) { + in, err := ioutil.ReadFile(file) + if err != nil { + panic("failed to read " + file + ": " + err.Error()) + } + exporter, err := New(CollectorIpsec, nil, 0, nil, log.NewNopLogger()) + if err != nil { + t.Fatalf("New() = _, %v; want nil", err) + } + exporter.scrape = func(e *Exporter) (m metrics, ok bool) { return e.scrapeLibreswan(in) } + outFile := strings.Replace(file, "-command.txt", "-metrics.txt", 1) + if _, err := os.Stat(outFile); err == nil { + out, err := ioutil.ReadFile(outFile) + if err != nil { + panic("failed to read " + outFile + ": " + err.Error()) + } + if err = testutil.CollectAndCompare(exporter, bytes.NewReader(out)); err != nil { + t.Errorf("testutil.CollectAndCompare() = %v; want nil", err) + } + } else { + if err = ioutil.WriteFile(outFile, collect(t, exporter), 0666); err != nil { + panic("failed to write " + outFile + ": " + err.Error()) + } + t.Logf("wrote %s golden master", outFile) + } + }) + } +} + +func TestExporter_Collect_Libreswan(t *testing.T) { + if testing.Short() { + t.Skip("skipping TestExporter_Collect_Libreswan during short test") + } + cmd, _ := shlex.Split("docker-compose -f ../testdata/docker/libreswan/docker-compose.yml exec -T moon /bin/sh -c 'ipsec status || true'") + b, err := ioutil.ReadFile("testdata/libreswan/metrics-integration.txt") + if err != nil { + panic("failed to read testdata/libreswan/metrics-integration.txt: " + err.Error()) + } + exporter, err := New(CollectorIpsec, nil, time.Second, cmd, log.NewNopLogger()) + if err != nil { + t.Fatalf("New() = _, %v; want nil", err) + } + if err := testutil.CollectAndCompare(&redactor{exporter}, bytes.NewReader(b)); err != nil { + t.Errorf("testutil.CollectAndCompare() = %v; want nil", err) + } +} diff --git a/exporter/metrics.go b/exporter/metrics.go index 81dbbc7..73ac8a2 100644 --- a/exporter/metrics.go +++ b/exporter/metrics.go @@ -7,11 +7,11 @@ type metrics struct { } type stats struct { - Uptime uptime `vici:"uptime"` - Workers workers `vici:"workers"` - Queues queues `vici:"queues"` - Scheduled uint64 `vici:"scheduled"` - IKESAs ikeSAs `vici:"ikesas"` + Uptime uptime `vici:"uptime"` + Workers *workers `vici:"workers"` + Queues *queues `vici:"queues"` + Scheduled *uint64 `vici:"scheduled"` + IKESAs ikeSAs `vici:"ikesas"` } type uptime struct { @@ -66,14 +66,14 @@ type ikeSA struct { type childSA struct { Name string `vici:"name"` UID uint32 `vici:"uniqueid"` - ReqID uint32 `vici:"reqid"` + ReqID *uint32 `vici:"reqid"` State string `vici:"state"` Mode string `vici:"mode"` Protocol string `vici:"protocol"` InBytes uint64 `vici:"bytes-in"` - InPackets uint64 `vici:"packets-in"` + InPackets *uint64 `vici:"packets-in"` OutBytes uint64 `vici:"bytes-out"` - OutPackets uint64 `vici:"packets-out"` + OutPackets *uint64 `vici:"packets-out"` Installed *int64 `vici:"install-time"` LocalTS []string `vici:"local-ts"` RemoteTS []string `vici:"remote-ts"` diff --git a/exporter/strongswan.go b/exporter/strongswan.go new file mode 100644 index 0000000..08f955a --- /dev/null +++ b/exporter/strongswan.go @@ -0,0 +1,235 @@ +package exporter + +import ( + "fmt" + "regexp" + "strconv" + "strings" +) + +const ( + ssPrefixStatus = "Status of IKE charon daemon" + ssPrefixPools = "Virtual IP pools (size/online/offline):" + ssPrefixSA = "Security Associations" +) + +var ( + ssIKESAStates = map[string]float64{ + "CREATED": 0, + "CONNECTING": 1, + "ESTABLISHED": 2, + "PASSIVE": 3, + "REKEYING": 4, + "REKEYED": 5, + "DELETING": 6, + "DESTROYING": 7, + } + ssChildSAStates = map[string]float64{ + "CREATED": 0, + "ROUTED": 1, + "INSTALLING": 2, + "INSTALLED": 3, + "UPDATING": 4, + "REKEYING": 5, + "REKEYED": 6, + "RETRYING": 7, + "DELETING": 8, + "DELETED": 9, + "DESTROYING": 10, + } +) + +var ( + ssUptimeRE = regexp.MustCompile(`^ uptime: .+, since (.+)$`) + ssStatsRE = regexp.MustCompile(`^ worker threads: (\d+) of (\d+) idle, (\d+)/(\d+)/(\d+)/(\d+) working, job queue: (\d+)/(\d+)/(\d+)/(\d+), scheduled: (\d+)$`) + ssPoolRE = regexp.MustCompile(`^ (.+?): (\d+)/(\d+)/(\d+)$`) + ssSAHeaderRE = regexp.MustCompile(`^Security Associations \((\d+) up, (\d+) connecting\):$`) + ssSAPrefixRE = regexp.MustCompile(`^\s*([^\[]+)\[(\d+)]: `) + ssSAStatusRE = regexp.MustCompile(`^([^ ]+) .+ ago, ([^\[]+)\[([^]]+)]\.\.\.([^\[]+)\[([^]]+)]$`) + ssSAVersionRE = regexp.MustCompile(`^(.+) SPIs:`) + ssSARemoteIdentityRE = regexp.MustCompile(`^Remote (.+) identity: (.+)$`) + ssChildSAPrefixRE = regexp.MustCompile(`^\s*([^{]+){(\d+)}: `) + ssChildSAStatusRE = regexp.MustCompile(`^([^,]+), ([^,]+), reqid (\d+), (.+) SPIs:.+`) + ssChildSATrafficRE = regexp.MustCompile(`(\d+) bytes_i(?: \((\d+) pkts?[^)]*\))?, (\d+) bytes_o(?: \((\d+) pkts?[^)]*\))?`) + ssChildSATSRE = regexp.MustCompile(`^ (.+) === (.+)$`) +) + +func (e *Exporter) scrapeStrongswan(b []byte) (m metrics, ok bool) { + // Looking for prefixes then scanning lines below for matching regexps + lines := strings.Split(string(b)+"\n", "\n") + for i, line := range lines { + switch { + case strings.HasPrefix(line, ssPrefixStatus): + j := i + if i+1 < len(lines) { + j++ + } + for _, line := range lines[j:] { + if !strings.HasPrefix(line, " ") { + break + } + matches := ssUptimeRE.FindStringSubmatch(line) + if matches != nil { + m.Stats.Uptime.Since = matches[1] + continue + } + matches = ssStatsRE.FindStringSubmatch(line) + if matches != nil { + m.Stats.Workers = &workers{} + m.Stats.Workers.Idle, _ = strconv.ParseUint(matches[1], 10, 64) + m.Stats.Workers.Total, _ = strconv.ParseUint(matches[2], 10, 64) + m.Stats.Workers.Active.Critical, _ = strconv.ParseUint(matches[3], 10, 64) + m.Stats.Workers.Active.High, _ = strconv.ParseUint(matches[4], 10, 64) + m.Stats.Workers.Active.Medium, _ = strconv.ParseUint(matches[5], 10, 64) + m.Stats.Workers.Active.Low, _ = strconv.ParseUint(matches[6], 10, 64) + m.Stats.Queues = &queues{} + m.Stats.Queues.Critical, _ = strconv.ParseUint(matches[7], 10, 64) + m.Stats.Queues.High, _ = strconv.ParseUint(matches[8], 10, 64) + m.Stats.Queues.Medium, _ = strconv.ParseUint(matches[9], 10, 64) + m.Stats.Queues.Low, _ = strconv.ParseUint(matches[10], 10, 64) + n, _ := strconv.ParseUint(matches[11], 10, 64) + m.Stats.Scheduled = &n + } + } + case line == ssPrefixPools: + j := i + if i+1 < len(lines) { + j++ + } + for _, line := range lines[j:] { + matches := ssPoolRE.FindStringSubmatch(line) + if matches == nil { + break + } + pool := pool{Address: matches[1]} + pool.Size, _ = strconv.ParseUint(matches[2], 10, 64) + pool.Online, _ = strconv.ParseUint(matches[3], 10, 64) + pool.Offline, _ = strconv.ParseUint(matches[4], 10, 64) + m.Pools = append(m.Pools, pool) + } + case strings.HasPrefix(line, ssPrefixSA): + matches := ssSAHeaderRE.FindStringSubmatch(line) + if matches != nil { + m.Stats.IKESAs.Total, _ = strconv.ParseUint(matches[1], 10, 64) + m.Stats.IKESAs.HalfOpen, _ = strconv.ParseUint(matches[2], 10, 64) + j := i + if i+1 < len(lines) { + j++ + } + var ( + prefix, childPrefix []string + sa, prevSA *ikeSA + childSA2 *childSA + ) + + Loop: + for _, line := range lines[j:] { + if (prefix != nil && !strings.HasPrefix(line, prefix[0])) || (childPrefix != nil && !strings.HasPrefix(line, childPrefix[0])) { + if sa != nil { + prevSA = sa + } + prefix, childPrefix, sa, childSA2 = nil, nil, nil, nil + } + if prefix == nil && childPrefix == nil { + matches = ssSAPrefixRE.FindStringSubmatch(line) + if matches != nil { + prefix = matches + sa = &ikeSA{ + Name: matches[1], + ChildSAs: make(map[string]*childSA), + } + n, _ := strconv.ParseUint(matches[2], 10, 32) + sa.UID = uint32(n) + m.IKESAs = append(m.IKESAs, sa) + + } else { + matches = ssChildSAPrefixRE.FindStringSubmatch(line) + if matches != nil { + childPrefix = matches + childSA2 = &childSA{Name: matches[1]} + n, _ := strconv.ParseUint(matches[2], 10, 32) + childSA2.UID = uint32(n) + if prevSA != nil { + prevSA.ChildSAs[fmt.Sprintf("%s-%d", childSA2.Name, childSA2.UID)] = childSA2 + } + } + } + } + switch { + case prefix != nil: + line = strings.TrimPrefix(line, prefix[0]) + matches = ssSAStatusRE.FindStringSubmatch(line) + if matches != nil { + sa.State = matches[1] + sa.LocalHost = matches[2] + sa.LocalID = matches[3] + sa.RemoteHost = matches[4] + sa.RemoteID = matches[5] + continue + } + matches = ssSAVersionRE.FindStringSubmatch(line) + if matches != nil { + switch matches[1] { + case "IKEv1": + sa.Version = 1 + case "IKEv2": + sa.Version = 2 + } + continue + } + matches = ssSARemoteIdentityRE.FindStringSubmatch(line) + if matches != nil { + if matches[1] == "XAuth" { + sa.RemoteXAuthID = matches[2] + } else { + sa.RemoteEAPID = matches[2] + } + } + case childPrefix != nil: + line = strings.TrimPrefix(line, childPrefix[0]) + matches = ssChildSAStatusRE.FindStringSubmatch(line) + if matches != nil { + childSA2.State = matches[1] + childSA2.Mode = matches[2] + n, _ := strconv.ParseUint(matches[3], 10, 64) + u := uint32(n) + childSA2.ReqID = &u + childSA2.Protocol = matches[4] + continue + } + matches = ssChildSATrafficRE.FindStringSubmatch(line) + if matches != nil { + childSA2.InBytes, _ = strconv.ParseUint(matches[1], 10, 64) + childSA2.OutBytes, _ = strconv.ParseUint(matches[3], 10, 64) + if matches[2] != "" && matches[4] != "" { + n, _ := strconv.ParseUint(matches[2], 10, 64) + childSA2.InPackets = &n + n, _ = strconv.ParseUint(matches[4], 10, 64) + childSA2.OutPackets = &n + } + continue + } + matches = ssChildSATSRE.FindStringSubmatch(line) + if matches != nil { + childSA2.LocalTS = strings.Split(matches[1], " ") + childSA2.RemoteTS = strings.Split(matches[2], " ") + } + default: + break Loop + } + } + } + } + } + ok = true + return +} + +func init() { + for k, v := range ssIKESAStates { + ikeSAStates[k] = v + } + for k, v := range ssChildSAStates { + childSAStates[k] = v + } +} diff --git a/exporter/strongswan_test.go b/exporter/strongswan_test.go new file mode 100644 index 0000000..7ea85b4 --- /dev/null +++ b/exporter/strongswan_test.go @@ -0,0 +1,98 @@ +package exporter + +import ( + "bytes" + "io/ioutil" + "net/url" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/go-kit/kit/log" + "github.com/google/shlex" + "github.com/prometheus/client_golang/prometheus/testutil" +) + +func TestExporter_scrapeStrongswan(t *testing.T) { + files, err := filepath.Glob("testdata/strongswan/*-command.txt") + if err != nil { + panic("failed to list test files: " + err.Error()) + } + for _, file := range files { + t.Run(file, func(t *testing.T) { + in, err := ioutil.ReadFile(file) + if err != nil { + panic("failed to read " + file + ": " + err.Error()) + } + exporter, err := New(CollectorIpsec, nil, 0, nil, log.NewNopLogger()) + if err != nil { + t.Fatalf("New() = _, %v; want nil", err) + } + exporter.scrape = func(e *Exporter) (m metrics, ok bool) { return e.scrapeStrongswan(in) } + outFile := strings.Replace(file, "-command.txt", "-metrics.txt", 1) + if _, err := os.Stat(outFile); err == nil { + out, err := ioutil.ReadFile(outFile) + if err != nil { + panic("failed to read " + outFile + ": " + err.Error()) + } + if err = testutil.CollectAndCompare(exporter, bytes.NewReader(out)); err != nil { + t.Errorf("testutil.CollectAndCompare() = %v; want nil", err) + } + } else { + if err = ioutil.WriteFile(outFile, collect(t, exporter), 0666); err != nil { + panic("failed to write " + outFile + ": " + err.Error()) + } + t.Logf("wrote %s golden master", outFile) + } + }) + } +} + +func TestExporter_Collect_Strongswan(t *testing.T) { + if testing.Short() { + t.Skip("skipping TestExporter_Collect_Strongswan during short test") + } + tests := []struct { + Name string + CollectorType int + }{ + { + Name: "VICI", + CollectorType: CollectorVICI, + }, + { + Name: "ipsec", + CollectorType: CollectorIpsec, + }, + } + address, _ := url.Parse("tcp://127.0.0.1:4502") + cmd, _ := shlex.Split("docker-compose -f ../testdata/docker/strongswan/docker-compose.yml exec -T moon /bin/sh -c 'ipsec statusall || true'") + b, err := ioutil.ReadFile("testdata/strongswan/metrics-integration.txt") + if err != nil { + panic("failed to read testdata/strongswan/metrics-integration.txt: " + err.Error()) + } + metricNames := []string{ + "ipsec_child_sa_bytes_in", + "ipsec_child_sa_bytes_out", + "ipsec_child_sa_packets_in", + "ipsec_child_sa_packets_out", + "ipsec_child_sa_state", + "ipsec_half_open_ike_sas", + "ipsec_ike_sa_state", + "ipsec_ike_sas", + "ipsec_up", + } + for _, td := range tests { + t.Run(td.Name, func(t *testing.T) { + exporter, err := New(td.CollectorType, address, time.Second, cmd, log.NewNopLogger()) + if err != nil { + t.Fatalf("New() = _, %v; want nil", err) + } + if err = testutil.CollectAndCompare(&redactor{exporter}, bytes.NewReader(b), metricNames...); err != nil { + t.Errorf("testutil.CollectAndCompare() = %v; want nil", err) + } + }) + } +} diff --git a/exporter/testdata/libreswan/1-command.txt b/exporter/testdata/libreswan/1-command.txt new file mode 100644 index 0000000..8575d68 --- /dev/null +++ b/exporter/testdata/libreswan/1-command.txt @@ -0,0 +1,126 @@ +000 using kernel interface: netkey +000 interface lo/lo 127.0.0.1:4500 +000 interface lo/lo 127.0.0.1:500 +000 interface eth0/eth0 192.0.2.254:4500 +000 interface eth0/eth0 192.0.2.254:500 +000 interface eth1/eth1 192.1.2.23:4500 +000 interface eth1/eth1 192.1.2.23:500 +000 +000 +000 fips mode=disabled; +000 SElinux=disabled +000 seccomp=disabled +000 +000 config setup options: +000 +000 configdir=/etc, configfile=/etc/ipsec.conf, secrets=/etc/ipsec.secrets, ipsecdir=/etc/ipsec.d +000 nssdir=/etc/ipsec.d, dumpdir=/tmp, statsbin=unset +000 dnssec-rootkey-file=/var/lib/unbound/root.key, dnssec-trusted= +000 sbindir=/usr/local/sbin, libexecdir=/usr/local/libexec/ipsec +000 pluto_version=v3.28-685-gbfd5aef521-master-s2, pluto_vendorid=OE-Libreswan-v3.28-685, audit-log=yes +000 nhelpers=-1, uniqueids=yes, dnssec-enable=yes, perpeerlog=no, logappend=no, logip=yes, shuntlifetime=900s, xfrmlifetime=30s +000 ddos-cookies-threshold=50000, ddos-max-halfopen=25000, ddos-mode=auto +000 ikeport=500, ikebuf=0, msg_errqueue=yes, strictcrlpolicy=no, crlcheckinterval=0, listen=, nflog-all=0 +000 ocsp-enable=no, ocsp-strict=no, ocsp-timeout=2, ocsp-uri= +000 ocsp-trust-name= +000 ocsp-cache-size=1000, ocsp-cache-min-age=3600, ocsp-cache-max-age=86400, ocsp-method=get +000 global-redirect=no, global-redirect-to= +000 secctx-attr-type=32001 +000 debug: base+cpu-usage +000 +000 nat-traversal=yes, keep-alive=20, nat-ikeport=4500 +000 virtual-private (%priv): +000 +000 Kernel algorithms supported: +000 +000 algorithm ESP encrypt: name=3DES_CBC, keysizemin=192, keysizemax=192 +000 algorithm ESP encrypt: name=AES_CBC, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_CCM_12, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_CCM_16, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_CCM_8, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_CTR, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_GCM_12, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_GCM_16, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_GCM_8, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=CAMELLIA_CBC, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=CHACHA20_POLY1305, keysizemin=256, keysizemax=256 +000 algorithm ESP encrypt: name=NULL, keysizemin=0, keysizemax=0 +000 algorithm ESP encrypt: name=NULL_AUTH_AES_GMAC, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=SERPENT_CBC, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=TWOFISH_CBC, keysizemin=128, keysizemax=256 +000 algorithm AH/ESP auth: name=AES_CMAC_96, key-length=128 +000 algorithm AH/ESP auth: name=AES_XCBC_96, key-length=128 +000 algorithm AH/ESP auth: name=HMAC_MD5_96, key-length=128 +000 algorithm AH/ESP auth: name=HMAC_SHA1_96, key-length=160 +000 algorithm AH/ESP auth: name=HMAC_SHA2_256_128, key-length=256 +000 algorithm AH/ESP auth: name=HMAC_SHA2_256_TRUNCBUG, key-length=256 +000 algorithm AH/ESP auth: name=HMAC_SHA2_384_192, key-length=384 +000 algorithm AH/ESP auth: name=HMAC_SHA2_512_256, key-length=512 +000 algorithm AH/ESP auth: name=NONE, key-length=0 +000 +000 IKE algorithms supported: +000 +000 algorithm IKE encrypt: v1id=5, v1name=OAKLEY_3DES_CBC, v2id=3, v2name=3DES, blocksize=8, keydeflen=192 +000 algorithm IKE encrypt: v1id=8, v1name=OAKLEY_CAMELLIA_CBC, v2id=23, v2name=CAMELLIA_CBC, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=-1, v1name=n/a, v2id=20, v2name=AES_GCM_C, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=-1, v1name=n/a, v2id=19, v2name=AES_GCM_B, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=-1, v1name=n/a, v2id=18, v2name=AES_GCM_A, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=13, v1name=OAKLEY_AES_CTR, v2id=13, v2name=AES_CTR, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=7, v1name=OAKLEY_AES_CBC, v2id=12, v2name=AES_CBC, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=65004, v1name=OAKLEY_SERPENT_CBC, v2id=65004, v2name=SERPENT_CBC, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=65005, v1name=OAKLEY_TWOFISH_CBC, v2id=65005, v2name=TWOFISH_CBC, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=65289, v1name=OAKLEY_TWOFISH_CBC_SSH, v2id=65289, v2name=TWOFISH_CBC_SSH, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=-1, v1name=n/a, v2id=28, v2name=CHACHA20_POLY1305, blocksize=16, keydeflen=256 +000 algorithm IKE PRF: name=HMAC_MD5, hashlen=16 +000 algorithm IKE PRF: name=HMAC_SHA1, hashlen=20 +000 algorithm IKE PRF: name=HMAC_SHA2_256, hashlen=32 +000 algorithm IKE PRF: name=HMAC_SHA2_384, hashlen=48 +000 algorithm IKE PRF: name=HMAC_SHA2_512, hashlen=64 +000 algorithm IKE PRF: name=AES_XCBC, hashlen=16 +000 algorithm IKE DH Key Exchange: name=MODP1536, bits=1536 +000 algorithm IKE DH Key Exchange: name=MODP2048, bits=2048 +000 algorithm IKE DH Key Exchange: name=MODP3072, bits=3072 +000 algorithm IKE DH Key Exchange: name=MODP4096, bits=4096 +000 algorithm IKE DH Key Exchange: name=MODP6144, bits=6144 +000 algorithm IKE DH Key Exchange: name=MODP8192, bits=8192 +000 algorithm IKE DH Key Exchange: name=DH19, bits=512 +000 algorithm IKE DH Key Exchange: name=DH20, bits=768 +000 algorithm IKE DH Key Exchange: name=DH21, bits=1056 +000 algorithm IKE DH Key Exchange: name=DH31, bits=256 +000 +000 stats db_ops: {curr_cnt, total_cnt, maxsz} :context={0,0,0} trans={0,0,0} attrs={0,0,0} +000 +000 Connection list: +000 +000 "westnet-eastnet-ah": 192.0.2.0/24===192.1.2.23<192.1.2.23>[@east]...192.1.2.45<192.1.2.45>[@west]===192.0.1.0/24; erouted; eroute owner: #2 +000 "westnet-eastnet-ah": oriented; my_ip=unset; their_ip=unset; my_updown=ipsec _updown; +000 "westnet-eastnet-ah": xauth us:none, xauth them:none, my_username=[any]; their_username=[any] +000 "westnet-eastnet-ah": our auth:rsasig, their auth:rsasig +000 "westnet-eastnet-ah": modecfg info: us:none, them:none, modecfg policy:push, dns:unset, domains:unset, banner:unset, cat:unset; +000 "westnet-eastnet-ah": labeled_ipsec:no; +000 "westnet-eastnet-ah": policy_label:unset; +000 "westnet-eastnet-ah": ike_life: 3600s; ipsec_life: 28800s; replay_window: 32; rekey_margin: 540s; rekey_fuzz: 100%; keyingtries: 0; +000 "westnet-eastnet-ah": retransmit-interval: 500ms; retransmit-timeout: 60s; +000 "westnet-eastnet-ah": initial-contact:no; cisco-unity:no; fake-strongswan:no; send-vendorid:no; send-no-esp-tfc:no; +000 "westnet-eastnet-ah": policy: RSASIG+AUTHENTICATE+TUNNEL+PFS+IKEV1_ALLOW+SAREF_TRACK+IKE_FRAG_ALLOW+ESN_NO; +000 "westnet-eastnet-ah": conn_prio: 24,24; interface: eth1; metric: 0; mtu: unset; sa_prio:auto; sa_tfc:none; +000 "westnet-eastnet-ah": nflog-group: unset; mark: unset; vti-iface:unset; vti-routing:no; vti-shared:no; nic-offload:auto; +000 "westnet-eastnet-ah": our idtype: ID_FQDN; our id=@east; their idtype: ID_FQDN; their id=@west +000 "westnet-eastnet-ah": dpd: action:hold; delay:0; timeout:0; nat-t: encaps:auto; nat_keepalive:yes; ikev1_natt:both +000 "westnet-eastnet-ah": newest ISAKMP SA: #1; newest IPsec SA: #2; +000 "westnet-eastnet-ah": IKEv1 algorithm newest: AES_CBC_256-HMAC_SHA2_256-MODP2048 +000 "westnet-eastnet-ah": AH algorithms: HMAC_SHA2_384_192 +000 "westnet-eastnet-ah": AH algorithm newest: HMAC_SHA2_384_192; pfsgroup= +000 +000 Total IPsec connections: loaded 1, active 1 +000 +000 State Information: DDoS cookies not required, Accepting new IKE connections +000 IKE SAs: total(1), half-open(0), open(0), authenticated(1), anonymous(0) +000 IPsec SAs: total(1), authenticated(1), anonymous(0) +000 +000 #1: "westnet-eastnet-ah":500 STATE_MAIN_R3 (sent MR3, ISAKMP SA established); EVENT_SA_REPLACE in 3326s; newest ISAKMP; lastdpd=-1s(seq in:0 out:0); idle; +000 #2: "westnet-eastnet-ah":500 STATE_QUICK_R2 (IPsec SA established); EVENT_SA_REPLACE in 28526s; newest IPSEC; eroute owner; isakmp#1; idle; +000 #2: "westnet-eastnet-ah" ah.b129f1f7@192.1.2.45 ah.e6f33a08@192.1.2.23 tun.0@192.1.2.45 tun.0@192.1.2.23 ref=0 refhim=0 Traffic: AHout=336B AHin=336B! AHmax=4194303B +000 +000 Bare Shunt list: +000 diff --git a/exporter/testdata/libreswan/1-metrics.txt b/exporter/testdata/libreswan/1-metrics.txt new file mode 100644 index 0000000..6f118f9 --- /dev/null +++ b/exporter/testdata/libreswan/1-metrics.txt @@ -0,0 +1,21 @@ +# HELP ipsec_child_sa_bytes_in Number of input bytes processed. +# TYPE ipsec_child_sa_bytes_in gauge +ipsec_child_sa_bytes_in{ike_sa_local_host="192.1.2.23",ike_sa_local_id="east",ike_sa_name="westnet-eastnet-ah",ike_sa_remote_host="192.1.2.45",ike_sa_remote_id="west",ike_sa_remote_identity="",ike_sa_uid="1",ike_sa_version="1",ike_sa_vips="",local_ts="192.0.2.0/24",mode="TUNNEL",name="westnet-eastnet-ah",protocol="AH",remote_ts="192.0.1.0/24",reqid="",uid="2"} 336 +# HELP ipsec_child_sa_bytes_out Number of output bytes processed. +# TYPE ipsec_child_sa_bytes_out gauge +ipsec_child_sa_bytes_out{ike_sa_local_host="192.1.2.23",ike_sa_local_id="east",ike_sa_name="westnet-eastnet-ah",ike_sa_remote_host="192.1.2.45",ike_sa_remote_id="west",ike_sa_remote_identity="",ike_sa_uid="1",ike_sa_version="1",ike_sa_vips="",local_ts="192.0.2.0/24",mode="TUNNEL",name="westnet-eastnet-ah",protocol="AH",remote_ts="192.0.1.0/24",reqid="",uid="2"} 336 +# HELP ipsec_child_sa_state Child SA state. +# TYPE ipsec_child_sa_state gauge +ipsec_child_sa_state{ike_sa_local_host="192.1.2.23",ike_sa_local_id="east",ike_sa_name="westnet-eastnet-ah",ike_sa_remote_host="192.1.2.45",ike_sa_remote_id="west",ike_sa_remote_identity="",ike_sa_uid="1",ike_sa_version="1",ike_sa_vips="",local_ts="192.0.2.0/24",mode="TUNNEL",name="westnet-eastnet-ah",protocol="AH",remote_ts="192.0.1.0/24",reqid="",uid="2"} 17 +# HELP ipsec_half_open_ike_sas Number of IKE SAs in half-open state. +# TYPE ipsec_half_open_ike_sas gauge +ipsec_half_open_ike_sas 0 +# HELP ipsec_ike_sa_state IKE SA state. +# TYPE ipsec_ike_sa_state gauge +ipsec_ike_sa_state{local_host="192.1.2.23",local_id="east",name="westnet-eastnet-ah",remote_host="192.1.2.45",remote_id="west",remote_identity="",uid="1",version="1",vips=""} 6 +# HELP ipsec_ike_sas Number of currently registered IKE SAs. +# TYPE ipsec_ike_sas gauge +ipsec_ike_sas 1 +# HELP ipsec_up Was the last scrape successful. +# TYPE ipsec_up gauge +ipsec_up 1 diff --git a/exporter/testdata/libreswan/2-command.txt b/exporter/testdata/libreswan/2-command.txt new file mode 100644 index 0000000..93d7ba4 --- /dev/null +++ b/exporter/testdata/libreswan/2-command.txt @@ -0,0 +1,133 @@ +000 using kernel interface: xfrm +000 +000 interface lo UDP 127.0.0.1:4500 +000 interface lo UDP 127.0.0.1:500 +000 interface eth0 UDP 192.1.3.209:4500 +000 interface eth0 UDP 192.1.3.209:500 +000 +000 fips mode=disabled; +000 SElinux=XXXXX +000 seccomp=OFF +000 +000 config setup options: +000 +000 configdir=/etc, configfile=/etc/ipsec.conf, secrets=/etc/ipsec.secrets, ipsecdir=/etc/ipsec.d +000 sbindir=PATH/sbin, libexecdir=PATH/libexec/ipsec +000 nhelpers=-1, uniqueids=yes, dnssec-enable=yes, logappend=no, logip=yes, shuntlifetime=900s, xfrmlifetime=30s +000 ddos-cookies-threshold=25000, ddos-max-halfopen=50000, ddos-mode=auto, ikev1-policy=accept +000 ikebuf=0, msg_errqueue=yes, crl-strict=no, crlcheckinterval=0, listen=, nflog-all=0 +000 ocsp-enable=no, ocsp-strict=no, ocsp-timeout=2, ocsp-uri= +000 ocsp-trust-name= +000 ocsp-cache-size=1000, ocsp-cache-min-age=3600, ocsp-cache-max-age=86400, ocsp-method=get +000 global-redirect=no, global-redirect-to= +000 secctx-attr-type=XXXX +000 debug ... +000 +000 nat-traversal=yes, keep-alive=20, nat-ikeport=4500 +000 virtual-private (%priv): +000 +000 Kernel algorithms supported: +000 +000 algorithm ESP encrypt: name=3DES_CBC, keysizemin=192, keysizemax=192 +000 algorithm ESP encrypt: name=AES_CBC, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_CCM_12, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_CCM_16, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_CCM_8, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_CTR, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_GCM_12, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_GCM_16, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_GCM_8, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=CAMELLIA_CBC, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=CHACHA20_POLY1305, keysizemin=256, keysizemax=256 +000 algorithm ESP encrypt: name=NULL, keysizemin=0, keysizemax=0 +000 algorithm ESP encrypt: name=NULL_AUTH_AES_GMAC, keysizemin=128, keysizemax=256 +000 algorithm AH/ESP auth: name=AES_CMAC_96, key-length=128 +000 algorithm AH/ESP auth: name=AES_XCBC_96, key-length=128 +000 algorithm AH/ESP auth: name=HMAC_MD5_96, key-length=128 +000 algorithm AH/ESP auth: name=HMAC_SHA1_96, key-length=160 +000 algorithm AH/ESP auth: name=HMAC_SHA2_256_128, key-length=256 +000 algorithm AH/ESP auth: name=HMAC_SHA2_256_TRUNCBUG, key-length=256 +000 algorithm AH/ESP auth: name=HMAC_SHA2_384_192, key-length=384 +000 algorithm AH/ESP auth: name=HMAC_SHA2_512_256, key-length=512 +000 algorithm AH/ESP auth: name=NONE, key-length=0 +000 +000 IKE algorithms supported: +000 +000 algorithm IKE encrypt: v1id=5, v1name=OAKLEY_3DES_CBC, v2id=3, v2name=3DES, blocksize=8, keydeflen=192 +000 algorithm IKE encrypt: v1id=8, v1name=OAKLEY_CAMELLIA_CBC, v2id=23, v2name=CAMELLIA_CBC, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=-1, v1name=n/a, v2id=20, v2name=AES_GCM_C, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=-1, v1name=n/a, v2id=19, v2name=AES_GCM_B, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=-1, v1name=n/a, v2id=18, v2name=AES_GCM_A, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=13, v1name=OAKLEY_AES_CTR, v2id=13, v2name=AES_CTR, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=7, v1name=OAKLEY_AES_CBC, v2id=12, v2name=AES_CBC, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=-1, v1name=n/a, v2id=28, v2name=CHACHA20_POLY1305, blocksize=16, keydeflen=256 +000 algorithm IKE PRF: name=HMAC_MD5, hashlen=16 +000 algorithm IKE PRF: name=HMAC_SHA1, hashlen=20 +000 algorithm IKE PRF: name=HMAC_SHA2_256, hashlen=32 +000 algorithm IKE PRF: name=HMAC_SHA2_384, hashlen=48 +000 algorithm IKE PRF: name=HMAC_SHA2_512, hashlen=64 +000 algorithm IKE PRF: name=AES_XCBC, hashlen=16 +000 algorithm IKE DH Key Exchange: name=MODP1536, bits=1536 +000 algorithm IKE DH Key Exchange: name=MODP2048, bits=2048 +000 algorithm IKE DH Key Exchange: name=MODP3072, bits=3072 +000 algorithm IKE DH Key Exchange: name=MODP4096, bits=4096 +000 algorithm IKE DH Key Exchange: name=MODP6144, bits=6144 +000 algorithm IKE DH Key Exchange: name=MODP8192, bits=8192 +000 algorithm IKE DH Key Exchange: name=DH19, bits=512 +000 algorithm IKE DH Key Exchange: name=DH20, bits=768 +000 algorithm IKE DH Key Exchange: name=DH21, bits=1056 +000 algorithm IKE DH Key Exchange: name=DH31, bits=256 +000 +000 stats db_ops: {curr_cnt, total_cnt, maxsz} :context={0,0,0} trans={0,0,0} attrs={0,0,0} +000 +000 Connection list: +000 +000 "road-east-x509-ipv4": 0.0.0.0/0===192.1.3.209[C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=road.testing.libreswan.org, E=user-road@testing.libreswan.org,+MC+S=C]---192.1.3.254...192.1.2.23<192.1.2.23>[C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=east.testing.libreswan.org, E=user-east@testing.libreswan.org]===0.0.0.0/0; unrouted; eroute owner: #0 +000 "road-east-x509-ipv4": oriented; my_ip=unset; their_ip=unset; mycert=road; peercert=east; my_updown=ipsec _updown; +000 "road-east-x509-ipv4": xauth us:none, xauth them:none, my_username=[any]; their_username=[any] +000 "road-east-x509-ipv4": our auth:rsasig, their auth:rsasig, our autheap:none, their autheap:none; +000 "road-east-x509-ipv4": modecfg info: us:client, them:none, modecfg policy:push, dns:unset, domains:unset, cat:unset; +000 "road-east-x509-ipv4": sec_label:unset; +000 "road-east-x509-ipv4": CAs: 'C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=Libreswan test CA for mainca, E=testing@libreswan.org'...'C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=Libreswan test CA for mainca, E=testing@libreswan.org' +000 "road-east-x509-ipv4": ike_life: 90s; ipsec_life: 28800s; replay_window: 128; rekey_margin: 10s; rekey_fuzz: 100%; keyingtries: 0; +000 "road-east-x509-ipv4": retransmit-interval: 9999ms; retransmit-timeout: 99s; iketcp:no; iketcp-port:4500; +000 "road-east-x509-ipv4": initial-contact:no; cisco-unity:no; fake-strongswan:no; send-vendorid:no; send-no-esp-tfc:no; +000 "road-east-x509-ipv4": policy: IKEv2+RSASIG+ECDSA+ENCRYPT+TUNNEL+PFS+IKEV2_ALLOW_NARROWING+IKE_FRAG_ALLOW+ESN_NO+ESN_YES+RSASIG_v1_5; +000 "road-east-x509-ipv4": v2-auth-hash-policy: SHA2_256+SHA2_384+SHA2_512; +000 "road-east-x509-ipv4": conn_prio: 0,0; interface: eth0; metric: 0; mtu: unset; sa_prio:auto; sa_tfc:none; +000 "road-east-x509-ipv4": nflog-group: unset; mark: unset; vti-iface:unset; vti-routing:no; vti-shared:no; nic-offload:auto; +000 "road-east-x509-ipv4": our idtype: ID_DER_ASN1_DN; our id=C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=road.testing.libreswan.org, E=user-road@testing.libreswan.org; their idtype: ID_DER_ASN1_DN; their id=C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=east.testing.libreswan.org, E=user-east@testing.libreswan.org +000 "road-east-x509-ipv4": dpd: action:hold; delay:0; timeout:0; nat-t: encaps:auto; nat_keepalive:yes; ikev1_natt:both +000 "road-east-x509-ipv4": newest ISAKMP SA: #0; newest IPsec SA: #0; conn serial: $1; +000 "road-east-x509-ipv4"[1]: 192.0.2.100/32===192.1.3.209[C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=road.testing.libreswan.org, E=user-road@testing.libreswan.org,+MC+S=C]---192.1.3.254...192.1.2.23<192.1.2.23>[C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=east.testing.libreswan.org, E=user-east@testing.libreswan.org]===0.0.0.0/0; erouted; eroute owner: #2 +000 "road-east-x509-ipv4"[1]: oriented; my_ip=192.0.2.100; their_ip=unset; mycert=road; peercert=east; my_updown=ipsec _updown; +000 "road-east-x509-ipv4"[1]: xauth us:none, xauth them:none, my_username=[any]; their_username=[any] +000 "road-east-x509-ipv4"[1]: our auth:rsasig, their auth:rsasig, our autheap:none, their autheap:none; +000 "road-east-x509-ipv4"[1]: modecfg info: us:client, them:none, modecfg policy:push, dns:unset, domains:unset, cat:unset; +000 "road-east-x509-ipv4"[1]: sec_label:unset; +000 "road-east-x509-ipv4"[1]: CAs: 'C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=Libreswan test CA for mainca, E=testing@libreswan.org'...'C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=Libreswan test CA for mainca, E=testing@libreswan.org' +000 "road-east-x509-ipv4"[1]: ike_life: 90s; ipsec_life: 28800s; replay_window: 128; rekey_margin: 10s; rekey_fuzz: 100%; keyingtries: 0; +000 "road-east-x509-ipv4"[1]: retransmit-interval: 9999ms; retransmit-timeout: 99s; iketcp:no; iketcp-port:4500; +000 "road-east-x509-ipv4"[1]: initial-contact:no; cisco-unity:no; fake-strongswan:no; send-vendorid:no; send-no-esp-tfc:no; +000 "road-east-x509-ipv4"[1]: policy: IKEv2+RSASIG+ECDSA+ENCRYPT+TUNNEL+PFS+UP+IKEV2_ALLOW_NARROWING+IKE_FRAG_ALLOW+ESN_NO+ESN_YES+RSASIG_v1_5; +000 "road-east-x509-ipv4"[1]: v2-auth-hash-policy: SHA2_256+SHA2_384+SHA2_512; +000 "road-east-x509-ipv4"[1]: conn_prio: 0,0; interface: eth0; metric: 0; mtu: unset; sa_prio:auto; sa_tfc:none; +000 "road-east-x509-ipv4"[1]: nflog-group: unset; mark: unset; vti-iface:unset; vti-routing:no; vti-shared:no; nic-offload:auto; +000 "road-east-x509-ipv4"[1]: our idtype: ID_DER_ASN1_DN; our id=C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=road.testing.libreswan.org, E=user-road@testing.libreswan.org; their idtype: ID_DER_ASN1_DN; their id=C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=east.testing.libreswan.org, E=user-east@testing.libreswan.org +000 "road-east-x509-ipv4"[1]: dpd: action:hold; delay:0; timeout:0; nat-t: encaps:auto; nat_keepalive:yes; ikev1_natt:both +000 "road-east-x509-ipv4"[1]: newest ISAKMP SA: #1; newest IPsec SA: #2; conn serial: $2, instantiated from: $1; +000 "road-east-x509-ipv4"[1]: IKEv2 algorithm newest: AES_GCM_16_256-HMAC_SHA2_512-MODP2048 +000 "road-east-x509-ipv4"[1]: ESP algorithm newest: AES_GCM_16_256-NONE; pfsgroup= +000 +000 Total IPsec connections: loaded 2, active 1 +000 +000 State Information: DDoS cookies not required, Accepting new IKE connections +000 IKE SAs: total(1), half-open(0), open(0), authenticated(1), anonymous(0) +000 IPsec SAs: total(1), authenticated(1), anonymous(0) +000 +000 #1: "road-east-x509-ipv4"[1] 192.1.2.23:500 STATE_V2_ESTABLISHED_IKE_SA (established IKE SA); REKEY in XXs; newest ISAKMP; idle; +000 #2: "road-east-x509-ipv4"[1] 192.1.2.23:500 STATE_V2_ESTABLISHED_CHILD_SA (established Child SA); REKEY in XXs; newest IPSEC; eroute owner; isakmp#1; idle; +000 #2: "road-east-x509-ipv4"[1] 192.1.2.23 esp.ESPSPIi@192.1.2.23 esp.ESPSPIi@192.1.3.209 tun.0@192.1.2.23 tun.0@192.1.3.209 Traffic: ESPin=84B ESPout=84B ESPmax=0B +000 +000 Bare Shunt list: +000 diff --git a/exporter/testdata/libreswan/2-metrics.txt b/exporter/testdata/libreswan/2-metrics.txt new file mode 100644 index 0000000..fab81c9 --- /dev/null +++ b/exporter/testdata/libreswan/2-metrics.txt @@ -0,0 +1,21 @@ +# HELP ipsec_child_sa_bytes_in Number of input bytes processed. +# TYPE ipsec_child_sa_bytes_in gauge +ipsec_child_sa_bytes_in{ike_sa_local_host="192.1.3.209",ike_sa_local_id="C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=road.testing.libreswan.org, E=user-road@testing.libreswan.org,+MC+S=C",ike_sa_name="road-east-x509-ipv4[1]",ike_sa_remote_host="192.1.2.23",ike_sa_remote_id="C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=east.testing.libreswan.org, E=user-east@testing.libreswan.org",ike_sa_remote_identity="",ike_sa_uid="1",ike_sa_version="2",ike_sa_vips="",local_ts="192.0.2.100/32",mode="TUNNEL",name="road-east-x509-ipv4[1]",protocol="ESP",remote_ts="0.0.0.0/0",reqid="",uid="2"} 84 +# HELP ipsec_child_sa_bytes_out Number of output bytes processed. +# TYPE ipsec_child_sa_bytes_out gauge +ipsec_child_sa_bytes_out{ike_sa_local_host="192.1.3.209",ike_sa_local_id="C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=road.testing.libreswan.org, E=user-road@testing.libreswan.org,+MC+S=C",ike_sa_name="road-east-x509-ipv4[1]",ike_sa_remote_host="192.1.2.23",ike_sa_remote_id="C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=east.testing.libreswan.org, E=user-east@testing.libreswan.org",ike_sa_remote_identity="",ike_sa_uid="1",ike_sa_version="2",ike_sa_vips="",local_ts="192.0.2.100/32",mode="TUNNEL",name="road-east-x509-ipv4[1]",protocol="ESP",remote_ts="0.0.0.0/0",reqid="",uid="2"} 84 +# HELP ipsec_child_sa_state Child SA state. +# TYPE ipsec_child_sa_state gauge +ipsec_child_sa_state{ike_sa_local_host="192.1.3.209",ike_sa_local_id="C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=road.testing.libreswan.org, E=user-road@testing.libreswan.org,+MC+S=C",ike_sa_name="road-east-x509-ipv4[1]",ike_sa_remote_host="192.1.2.23",ike_sa_remote_id="C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=east.testing.libreswan.org, E=user-east@testing.libreswan.org",ike_sa_remote_identity="",ike_sa_uid="1",ike_sa_version="2",ike_sa_vips="",local_ts="192.0.2.100/32",mode="TUNNEL",name="road-east-x509-ipv4[1]",protocol="ESP",remote_ts="0.0.0.0/0",reqid="",uid="2"} 46 +# HELP ipsec_half_open_ike_sas Number of IKE SAs in half-open state. +# TYPE ipsec_half_open_ike_sas gauge +ipsec_half_open_ike_sas 0 +# HELP ipsec_ike_sa_state IKE SA state. +# TYPE ipsec_ike_sa_state gauge +ipsec_ike_sa_state{local_host="192.1.3.209",local_id="C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=road.testing.libreswan.org, E=user-road@testing.libreswan.org,+MC+S=C",name="road-east-x509-ipv4[1]",remote_host="192.1.2.23",remote_id="C=CA, ST=Ontario, L=Toronto, O=Libreswan, OU=Test Department, CN=east.testing.libreswan.org, E=user-east@testing.libreswan.org",remote_identity="",uid="1",version="2",vips=""} 45 +# HELP ipsec_ike_sas Number of currently registered IKE SAs. +# TYPE ipsec_ike_sas gauge +ipsec_ike_sas 1 +# HELP ipsec_up Was the last scrape successful. +# TYPE ipsec_up gauge +ipsec_up 1 diff --git a/exporter/testdata/libreswan/3-command.txt b/exporter/testdata/libreswan/3-command.txt new file mode 100644 index 0000000..efc5fdc --- /dev/null +++ b/exporter/testdata/libreswan/3-command.txt @@ -0,0 +1,113 @@ +000 using kernel interface: xfrm +000 +000 interface lo UDP 127.0.0.1:4500 +000 interface lo UDP 127.0.0.1:500 +000 interface eth0 UDP 172.31.1.2:4500 +000 interface eth0 UDP 172.31.1.2:500 +000 +000 fips mode=disabled; +000 SElinux=disabled +000 seccomp=unsupported +000 +000 config setup options: +000 +000 configdir=/etc, configfile=/etc/ipsec.conf, secrets=/etc/ipsec.secrets, ipsecdir=/etc/ipsec.d +000 nssdir=/var/lib/ipsec/nss, dumpdir=/run/pluto, statsbin=unset +000 dnssec-rootkey-file=/usr/share/dns/root.key, dnssec-trusted= +000 sbindir=/usr/sbin, libexecdir=/usr/libexec/ipsec +000 pluto_version=4.3, pluto_vendorid=OE-Libreswan-4.3, audit-log=yes +000 nhelpers=-1, uniqueids=yes, dnssec-enable=yes, logappend=yes, logip=yes, shuntlifetime=900s, xfrmlifetime=30s +000 ddos-cookies-threshold=25000, ddos-max-halfopen=50000, ddos-mode=auto, ikev1-policy=accept +000 ikebuf=0, msg_errqueue=yes, crl-strict=no, crlcheckinterval=0, listen=, nflog-all=0 +000 ocsp-enable=no, ocsp-strict=no, ocsp-timeout=2, ocsp-uri= +000 ocsp-trust-name= +000 ocsp-cache-size=1000, ocsp-cache-min-age=3600, ocsp-cache-max-age=86400, ocsp-method=get +000 global-redirect=no, global-redirect-to= +000 secctx-attr-type=32001 +000 debug: +000 +000 nat-traversal=yes, keep-alive=20, nat-ikeport=4500 +000 virtual-private (%priv): +000 +000 Kernel algorithms supported: +000 +000 algorithm ESP encrypt: name=3DES_CBC, keysizemin=192, keysizemax=192 +000 algorithm ESP encrypt: name=AES_CBC, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_CCM_12, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_CCM_16, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_CCM_8, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_CTR, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_GCM_12, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_GCM_16, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=AES_GCM_8, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=CAMELLIA_CBC, keysizemin=128, keysizemax=256 +000 algorithm ESP encrypt: name=CHACHA20_POLY1305, keysizemin=256, keysizemax=256 +000 algorithm ESP encrypt: name=NULL, keysizemin=0, keysizemax=0 +000 algorithm ESP encrypt: name=NULL_AUTH_AES_GMAC, keysizemin=128, keysizemax=256 +000 algorithm AH/ESP auth: name=AES_CMAC_96, key-length=128 +000 algorithm AH/ESP auth: name=AES_XCBC_96, key-length=128 +000 algorithm AH/ESP auth: name=HMAC_MD5_96, key-length=128 +000 algorithm AH/ESP auth: name=HMAC_SHA1_96, key-length=160 +000 algorithm AH/ESP auth: name=HMAC_SHA2_256_128, key-length=256 +000 algorithm AH/ESP auth: name=HMAC_SHA2_256_TRUNCBUG, key-length=256 +000 algorithm AH/ESP auth: name=HMAC_SHA2_384_192, key-length=384 +000 algorithm AH/ESP auth: name=HMAC_SHA2_512_256, key-length=512 +000 algorithm AH/ESP auth: name=NONE, key-length=0 +000 +000 IKE algorithms supported: +000 +000 algorithm IKE encrypt: v1id=5, v1name=OAKLEY_3DES_CBC, v2id=3, v2name=3DES, blocksize=8, keydeflen=192 +000 algorithm IKE encrypt: v1id=8, v1name=OAKLEY_CAMELLIA_CBC, v2id=23, v2name=CAMELLIA_CBC, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=-1, v1name=n/a, v2id=20, v2name=AES_GCM_C, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=-1, v1name=n/a, v2id=19, v2name=AES_GCM_B, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=-1, v1name=n/a, v2id=18, v2name=AES_GCM_A, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=13, v1name=OAKLEY_AES_CTR, v2id=13, v2name=AES_CTR, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=7, v1name=OAKLEY_AES_CBC, v2id=12, v2name=AES_CBC, blocksize=16, keydeflen=128 +000 algorithm IKE encrypt: v1id=-1, v1name=n/a, v2id=28, v2name=CHACHA20_POLY1305, blocksize=16, keydeflen=256 +000 algorithm IKE PRF: name=HMAC_MD5, hashlen=16 +000 algorithm IKE PRF: name=HMAC_SHA1, hashlen=20 +000 algorithm IKE PRF: name=HMAC_SHA2_256, hashlen=32 +000 algorithm IKE PRF: name=HMAC_SHA2_384, hashlen=48 +000 algorithm IKE PRF: name=HMAC_SHA2_512, hashlen=64 +000 algorithm IKE PRF: name=AES_XCBC, hashlen=16 +000 algorithm IKE DH Key Exchange: name=MODP1536, bits=1536 +000 algorithm IKE DH Key Exchange: name=MODP2048, bits=2048 +000 algorithm IKE DH Key Exchange: name=MODP3072, bits=3072 +000 algorithm IKE DH Key Exchange: name=MODP4096, bits=4096 +000 algorithm IKE DH Key Exchange: name=MODP6144, bits=6144 +000 algorithm IKE DH Key Exchange: name=MODP8192, bits=8192 +000 algorithm IKE DH Key Exchange: name=DH19, bits=512 +000 algorithm IKE DH Key Exchange: name=DH20, bits=768 +000 algorithm IKE DH Key Exchange: name=DH21, bits=1056 +000 algorithm IKE DH Key Exchange: name=DH31, bits=256 +000 +000 stats db_ops: {curr_cnt, total_cnt, maxsz} :context={0,0,0} trans={0,0,0} attrs={0,0,0} +000 +000 Connection list: +000 +000 "host-host": 172.31.1.2<172.31.1.2>...172.31.1.1<172.31.1.1>; unrouted; eroute owner: #0 +000 "host-host": oriented; my_ip=unset; their_ip=unset; my_updown=ipsec _updown; +000 "host-host": xauth us:none, xauth them:none, my_username=[any]; their_username=[any] +000 "host-host": our auth:secret, their auth:secret +000 "host-host": modecfg info: us:none, them:none, modecfg policy:push, dns:unset, domains:unset, cat:unset; +000 "host-host": sec_label:unset; +000 "host-host": ike_life: 28800s; ipsec_life: 28800s; replay_window: 32; rekey_margin: 5400s; rekey_fuzz: 100%; keyingtries: 0; +000 "host-host": retransmit-interval: 500ms; retransmit-timeout: 60s; iketcp:no; iketcp-port:4500; +000 "host-host": initial-contact:no; cisco-unity:no; fake-strongswan:no; send-vendorid:no; send-no-esp-tfc:no; +000 "host-host": policy: IKEv2+PSK+ENCRYPT+TUNNEL+PFS+IKE_FRAG_ALLOW+ESN_NO; +000 "host-host": v2-auth-hash-policy: none; +000 "host-host": conn_prio: 32,32; interface: eth0; metric: 0; mtu: unset; sa_prio:auto; sa_tfc:none; +000 "host-host": nflog-group: unset; mark: unset; vti-iface:unset; vti-routing:no; vti-shared:no; nic-offload:auto; +000 "host-host": our idtype: ID_IPV4_ADDR; our id=172.31.1.2; their idtype: ID_IPV4_ADDR; their id=172.31.1.1 +000 "host-host": dpd: action:hold; delay:0; timeout:0; nat-t: encaps:auto; nat_keepalive:yes; ikev1_natt:both +000 "host-host": newest ISAKMP SA: #0; newest IPsec SA: #0; conn serial: $2; +000 "host-host": ESP algorithms: AES_CBC_256-AES_XCBC_96 +000 +000 Total IPsec connections: loaded 1, active 0 +000 +000 State Information: DDoS cookies not required, Accepting new IKE connections +000 IKE SAs: total(0), half-open(0), open(0), authenticated(0), anonymous(0) +000 IPsec SAs: total(0), authenticated(0), anonymous(0) +000 +000 Bare Shunt list: +000 diff --git a/exporter/testdata/libreswan/3-metrics.txt b/exporter/testdata/libreswan/3-metrics.txt new file mode 100644 index 0000000..5d096e0 --- /dev/null +++ b/exporter/testdata/libreswan/3-metrics.txt @@ -0,0 +1,9 @@ +# HELP ipsec_half_open_ike_sas Number of IKE SAs in half-open state. +# TYPE ipsec_half_open_ike_sas gauge +ipsec_half_open_ike_sas 0 +# HELP ipsec_ike_sas Number of currently registered IKE SAs. +# TYPE ipsec_ike_sas gauge +ipsec_ike_sas 0 +# HELP ipsec_up Was the last scrape successful. +# TYPE ipsec_up gauge +ipsec_up 1 diff --git a/exporter/testdata/libreswan/metrics-integration.txt b/exporter/testdata/libreswan/metrics-integration.txt new file mode 100644 index 0000000..6023ac2 --- /dev/null +++ b/exporter/testdata/libreswan/metrics-integration.txt @@ -0,0 +1,21 @@ +# HELP ipsec_child_sa_bytes_in Number of input bytes processed. +# TYPE ipsec_child_sa_bytes_in gauge +ipsec_child_sa_bytes_in{ike_sa_local_host="172.31.1.1",ike_sa_local_id="",ike_sa_name="host-host",ike_sa_remote_host="172.31.1.2",ike_sa_remote_id="",ike_sa_remote_identity="",ike_sa_uid="X",ike_sa_version="2",ike_sa_vips="",local_ts="",mode="TUNNEL",name="host-host",protocol="ESP",remote_ts="",reqid="",uid="X"} 84 +# HELP ipsec_child_sa_bytes_out Number of output bytes processed. +# TYPE ipsec_child_sa_bytes_out gauge +ipsec_child_sa_bytes_out{ike_sa_local_host="172.31.1.1",ike_sa_local_id="",ike_sa_name="host-host",ike_sa_remote_host="172.31.1.2",ike_sa_remote_id="",ike_sa_remote_identity="",ike_sa_uid="X",ike_sa_version="2",ike_sa_vips="",local_ts="",mode="TUNNEL",name="host-host",protocol="ESP",remote_ts="",reqid="",uid="X"} 84 +# HELP ipsec_child_sa_state Child SA state. +# TYPE ipsec_child_sa_state gauge +ipsec_child_sa_state{ike_sa_local_host="172.31.1.1",ike_sa_local_id="",ike_sa_name="host-host",ike_sa_remote_host="172.31.1.2",ike_sa_remote_id="",ike_sa_remote_identity="",ike_sa_uid="X",ike_sa_version="2",ike_sa_vips="",local_ts="",mode="TUNNEL",name="host-host",protocol="ESP",remote_ts="",reqid="",uid="X"} 46 +# HELP ipsec_half_open_ike_sas Number of IKE SAs in half-open state. +# TYPE ipsec_half_open_ike_sas gauge +ipsec_half_open_ike_sas 0 +# HELP ipsec_ike_sa_state IKE SA state. +# TYPE ipsec_ike_sa_state gauge +ipsec_ike_sa_state{local_host="172.31.1.1",local_id="",name="host-host",remote_host="172.31.1.2",remote_id="",remote_identity="",uid="X",version="2",vips=""} 45 +# HELP ipsec_ike_sas Number of currently registered IKE SAs. +# TYPE ipsec_ike_sas gauge +ipsec_ike_sas 1 +# HELP ipsec_up Was the last scrape successful. +# TYPE ipsec_up gauge +ipsec_up 1 diff --git a/exporter/testdata/metrics-1.txt b/exporter/testdata/metrics.txt similarity index 100% rename from exporter/testdata/metrics-1.txt rename to exporter/testdata/metrics.txt diff --git a/exporter/testdata/strongswan/1-command.txt b/exporter/testdata/strongswan/1-command.txt new file mode 100644 index 0000000..cdc41da --- /dev/null +++ b/exporter/testdata/strongswan/1-command.txt @@ -0,0 +1,19 @@ +Status of IKE charon daemon (strongSwan 5.5.3, Linux 3.10.0-693.11.6.el7.x86_64, x86_64): + uptime: 6 minutes, since Mar 14 10:28:49 2018 + malloc: sbrk 1622016, mmap 0, used 512480, free 1109536 + worker threads: 11 of 16 idle, 5/0/0/0 working, job queue: 0/0/0/0, scheduled: 3 + loaded plugins: charon aes des rc2 sha2 sha1 md4 md5 random nonce x509 revocation constraints acert pubkey pkcs1 pkcs8 pkcs12 pgp dnskey sshkey pem openssl gcrypt fips-prf gmp curve25519 xcbc cmac hmac ctr ccm gcm curl attr kernel-netlink resolve socket-default farp stroke vici updown eap-identity eap-md5 eap-gtc eap-mschapv2 eap-tls eap-ttls eap-peap xauth-generic xauth-eap xauth-pam xauth-noauth dhcp unity +Virtual IP pools (size/online/offline): + 10.42.42.0/24: 254/0/0 +Listening IP addresses: + 173.44.45.44 +Connections: + kelvic-mtn: 173.44.45.44...41.220.79.242 IKEv1, dpddelay=30s + kelvic-mtn: local: [173.44.45.44] uses pre-shared key authentication + kelvic-mtn: remote: [41.220.79.242] uses pre-shared key authentication + kelvic-mtn: child: 192.168.2.0/24 === 10.2.0.0/24 TUNNEL, dpdaction=restart +Security Associations (1 up, 0 connecting): + kelvic-mtn[1]: ESTABLISHED 6 minutes ago, 173.44.45.44[173.44.45.44]...41.220.79.242[41.220.79.242] + kelvic-mtn[1]: IKEv1 SPIs: 43cc5f77aa48bbc1_i* 9748fb98f4d0ba94_r, pre-shared key reauthentication in 2 hours + kelvic-mtn[1]: IKE proposal: AES_CBC_128/HMAC_MD5_96/PRF_HMAC_MD5/MODP_1024 + kelvic-mtn[1]: Tasks queued: QUICK_MODE diff --git a/exporter/testdata/strongswan/1-metrics.txt b/exporter/testdata/strongswan/1-metrics.txt new file mode 100644 index 0000000..31eab33 --- /dev/null +++ b/exporter/testdata/strongswan/1-metrics.txt @@ -0,0 +1,39 @@ +# HELP ipsec_active_workers Number of threads processing jobs. +# TYPE ipsec_active_workers gauge +ipsec_active_workers 5 +# HELP ipsec_half_open_ike_sas Number of IKE SAs in half-open state. +# TYPE ipsec_half_open_ike_sas gauge +ipsec_half_open_ike_sas 0 +# HELP ipsec_idle_workers Number of idle worker threads. +# TYPE ipsec_idle_workers gauge +ipsec_idle_workers 11 +# HELP ipsec_ike_sa_state IKE SA state. +# TYPE ipsec_ike_sa_state gauge +ipsec_ike_sa_state{local_host="173.44.45.44",local_id="173.44.45.44",name="kelvic-mtn",remote_host="41.220.79.242",remote_id="41.220.79.242",remote_identity="",uid="1",version="1",vips=""} 2 +# HELP ipsec_ike_sas Number of currently registered IKE SAs. +# TYPE ipsec_ike_sas gauge +ipsec_ike_sas 1 +# HELP ipsec_offline_pool_ips Number of leases offline. +# TYPE ipsec_offline_pool_ips gauge +ipsec_offline_pool_ips{address="10.42.42.0/24",name=""} 0 +# HELP ipsec_online_pool_ips Number of leases online. +# TYPE ipsec_online_pool_ips gauge +ipsec_online_pool_ips{address="10.42.42.0/24",name=""} 0 +# HELP ipsec_pool_ips_total Number of addresses in the pool. +# TYPE ipsec_pool_ips_total gauge +ipsec_pool_ips_total{address="10.42.42.0/24",name=""} 254 +# HELP ipsec_queues Number of queued jobs. +# TYPE ipsec_queues gauge +ipsec_queues{priority="critical"} 0 +ipsec_queues{priority="high"} 0 +ipsec_queues{priority="low"} 0 +ipsec_queues{priority="medium"} 0 +# HELP ipsec_up Was the last scrape successful. +# TYPE ipsec_up gauge +ipsec_up 1 +# HELP ipsec_uptime_seconds Number of seconds since the daemon started. +# TYPE ipsec_uptime_seconds gauge +ipsec_uptime_seconds -1.521023329e+09 +# HELP ipsec_workers_total Number of worker threads. +# TYPE ipsec_workers_total gauge +ipsec_workers_total 16 diff --git a/exporter/testdata/strongswan/2-command.txt b/exporter/testdata/strongswan/2-command.txt new file mode 100644 index 0000000..505bf03 --- /dev/null +++ b/exporter/testdata/strongswan/2-command.txt @@ -0,0 +1,21 @@ +Status of IKE charon daemon (strongSwan 5.5.3, Linux 4.4.0-93-generic, x86_64): + uptime: 101 seconds, since Aug 30 17:21:42 2017 + malloc: sbrk 2297856, mmap 0, used 290096, free 2007760 + worker threads: 11 of 16 idle, 5/0/0/0 working, job queue: 0/0/0/0, scheduled: 3 + loaded plugins: charon aes des rc2 sha2 sha1 md4 md5 random nonce x509 constraints pubkey pkcs1 pkcs7 pkcs8 pkcs12 pgp dnskey sshkey pem fips-prf gmp curve25519 xcbc cmac hmac attr kernel-netlink resolve socket-default stroke vici updown eap-identity eap-mschapv2 eap-radius xauth-generic +Listening IP addresses: + 162.23.112.110 + 10.10.0.6 + 172.17.0.1 +Connections: +vpnikev2: 162.23.112.110...45.81.93.15 IKEv2 +vpnikev2: local: [162.23.112.110] uses EAP_MSCHAPV2 authentication with EAP identity 'monitor' +vpnikev2: remote: [monitor] uses public key authentication +vpnikev2: child: dynamic === 0.0.0.0/0 TRANSPORT +Security Associations (1 up, 0 connecting): +vpnikev2[1]: ESTABLISHED 92 seconds ago, 162.23.112.110[162.23.112.110]...45.81.93.15[monitor] +vpnikev2[1]: IKEv2 SPIs: 6c69be09930627c6_i* 0df6e74078fdbce0_r, EAP reauthentication in 2 hours +vpnikev2[1]: IKE proposal: AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_3072 +vpnikev2{1}: INSTALLED, TUNNEL, reqid 1, ESP SPIs: cecde7c5_i ca578af0_o +vpnikev2{1}: AES_CBC_128/HMAC_SHA2_256_128, 0 bytes_i, 0 bytes_o, rekeying in 44 minutes +vpnikev2{1}: 192.168.50.14/32 === 45.81.93.15/32 diff --git a/exporter/testdata/strongswan/2-metrics.txt b/exporter/testdata/strongswan/2-metrics.txt new file mode 100644 index 0000000..1decf23 --- /dev/null +++ b/exporter/testdata/strongswan/2-metrics.txt @@ -0,0 +1,39 @@ +# HELP ipsec_active_workers Number of threads processing jobs. +# TYPE ipsec_active_workers gauge +ipsec_active_workers 5 +# HELP ipsec_child_sa_bytes_in Number of input bytes processed. +# TYPE ipsec_child_sa_bytes_in gauge +ipsec_child_sa_bytes_in{ike_sa_local_host="162.23.112.110",ike_sa_local_id="162.23.112.110",ike_sa_name="vpnikev2",ike_sa_remote_host="45.81.93.15",ike_sa_remote_id="monitor",ike_sa_remote_identity="",ike_sa_uid="1",ike_sa_version="2",ike_sa_vips="",local_ts="192.168.50.14/32",mode="TUNNEL",name="vpnikev2",protocol="ESP",remote_ts="45.81.93.15/32",reqid="1",uid="1"} 0 +# HELP ipsec_child_sa_bytes_out Number of output bytes processed. +# TYPE ipsec_child_sa_bytes_out gauge +ipsec_child_sa_bytes_out{ike_sa_local_host="162.23.112.110",ike_sa_local_id="162.23.112.110",ike_sa_name="vpnikev2",ike_sa_remote_host="45.81.93.15",ike_sa_remote_id="monitor",ike_sa_remote_identity="",ike_sa_uid="1",ike_sa_version="2",ike_sa_vips="",local_ts="192.168.50.14/32",mode="TUNNEL",name="vpnikev2",protocol="ESP",remote_ts="45.81.93.15/32",reqid="1",uid="1"} 0 +# HELP ipsec_child_sa_state Child SA state. +# TYPE ipsec_child_sa_state gauge +ipsec_child_sa_state{ike_sa_local_host="162.23.112.110",ike_sa_local_id="162.23.112.110",ike_sa_name="vpnikev2",ike_sa_remote_host="45.81.93.15",ike_sa_remote_id="monitor",ike_sa_remote_identity="",ike_sa_uid="1",ike_sa_version="2",ike_sa_vips="",local_ts="192.168.50.14/32",mode="TUNNEL",name="vpnikev2",protocol="ESP",remote_ts="45.81.93.15/32",reqid="1",uid="1"} 3 +# HELP ipsec_half_open_ike_sas Number of IKE SAs in half-open state. +# TYPE ipsec_half_open_ike_sas gauge +ipsec_half_open_ike_sas 0 +# HELP ipsec_idle_workers Number of idle worker threads. +# TYPE ipsec_idle_workers gauge +ipsec_idle_workers 11 +# HELP ipsec_ike_sa_state IKE SA state. +# TYPE ipsec_ike_sa_state gauge +ipsec_ike_sa_state{local_host="162.23.112.110",local_id="162.23.112.110",name="vpnikev2",remote_host="45.81.93.15",remote_id="monitor",remote_identity="",uid="1",version="2",vips=""} 2 +# HELP ipsec_ike_sas Number of currently registered IKE SAs. +# TYPE ipsec_ike_sas gauge +ipsec_ike_sas 1 +# HELP ipsec_queues Number of queued jobs. +# TYPE ipsec_queues gauge +ipsec_queues{priority="critical"} 0 +ipsec_queues{priority="high"} 0 +ipsec_queues{priority="low"} 0 +ipsec_queues{priority="medium"} 0 +# HELP ipsec_up Was the last scrape successful. +# TYPE ipsec_up gauge +ipsec_up 1 +# HELP ipsec_uptime_seconds Number of seconds since the daemon started. +# TYPE ipsec_uptime_seconds gauge +ipsec_uptime_seconds -1.504113702e+09 +# HELP ipsec_workers_total Number of worker threads. +# TYPE ipsec_workers_total gauge +ipsec_workers_total 16 diff --git a/exporter/testdata/strongswan/3-command.txt b/exporter/testdata/strongswan/3-command.txt new file mode 100644 index 0000000..3edfb5b --- /dev/null +++ b/exporter/testdata/strongswan/3-command.txt @@ -0,0 +1,14 @@ +Status of IKE charon daemon (strongSwan 5.9.1, Linux 5.4.39-linuxkit, x86_64): + uptime: 52 seconds, since Nov 13 11:13:34 2021 + malloc: sbrk 2433024, mmap 0, used 551888, free 1881136 + worker threads: 11 of 16 idle, 5/0/0/0 working, job queue: 0/0/0/0, scheduled: 0 + loaded plugins: charon aesni aes rc2 sha2 sha1 md5 mgf1 random nonce x509 revocation constraints pubkey pkcs1 pkcs7 pkcs8 pkcs12 pgp dnskey sshkey pem openssl fips-prf gmp agent xcbc hmac gcm drbg attr kernel-netlink resolve socket-default connmark stroke vici updown eap-mschapv2 xauth-generic counters +Listening IP addresses: + 172.31.0.2 +Connections: + host-host: sun...moon IKEv2 + host-host: local: [sun] uses pre-shared key authentication + host-host: remote: [moon] uses pre-shared key authentication + host-host: child: dynamic === dynamic TRANSPORT +Security Associations (0 up, 0 connecting): + none diff --git a/exporter/testdata/strongswan/3-metrics.txt b/exporter/testdata/strongswan/3-metrics.txt new file mode 100644 index 0000000..807fd73 --- /dev/null +++ b/exporter/testdata/strongswan/3-metrics.txt @@ -0,0 +1,27 @@ +# HELP ipsec_active_workers Number of threads processing jobs. +# TYPE ipsec_active_workers gauge +ipsec_active_workers 5 +# HELP ipsec_half_open_ike_sas Number of IKE SAs in half-open state. +# TYPE ipsec_half_open_ike_sas gauge +ipsec_half_open_ike_sas 0 +# HELP ipsec_idle_workers Number of idle worker threads. +# TYPE ipsec_idle_workers gauge +ipsec_idle_workers 11 +# HELP ipsec_ike_sas Number of currently registered IKE SAs. +# TYPE ipsec_ike_sas gauge +ipsec_ike_sas 0 +# HELP ipsec_queues Number of queued jobs. +# TYPE ipsec_queues gauge +ipsec_queues{priority="critical"} 0 +ipsec_queues{priority="high"} 0 +ipsec_queues{priority="low"} 0 +ipsec_queues{priority="medium"} 0 +# HELP ipsec_up Was the last scrape successful. +# TYPE ipsec_up gauge +ipsec_up 1 +# HELP ipsec_uptime_seconds Number of seconds since the daemon started. +# TYPE ipsec_uptime_seconds gauge +ipsec_uptime_seconds -1.636802014e+09 +# HELP ipsec_workers_total Number of worker threads. +# TYPE ipsec_workers_total gauge +ipsec_workers_total 16 diff --git a/exporter/testdata/metrics-2.txt b/exporter/testdata/strongswan/metrics-integration.txt similarity index 81% rename from exporter/testdata/metrics-2.txt rename to exporter/testdata/strongswan/metrics-integration.txt index f6491eb..8613da4 100644 --- a/exporter/testdata/metrics-2.txt +++ b/exporter/testdata/strongswan/metrics-integration.txt @@ -1,24 +1,24 @@ # HELP ipsec_child_sa_bytes_in Number of input bytes processed. # TYPE ipsec_child_sa_bytes_in gauge -ipsec_child_sa_bytes_in{ike_sa_local_host="172.31.0.1",ike_sa_local_id="moon",ike_sa_name="host-host",ike_sa_remote_host="172.31.0.2",ike_sa_remote_id="sun",ike_sa_remote_identity="",ike_sa_uid="1",ike_sa_version="2",ike_sa_vips="",local_ts="172.31.0.1/32",mode="TRANSPORT",name="host-host",protocol="AH",remote_ts="172.31.0.2/32",reqid="1",uid="1"} 64 +ipsec_child_sa_bytes_in{ike_sa_local_host="172.31.0.1",ike_sa_local_id="moon",ike_sa_name="host-host",ike_sa_remote_host="172.31.0.2",ike_sa_remote_id="sun",ike_sa_remote_identity="",ike_sa_uid="X",ike_sa_version="2",ike_sa_vips="",local_ts="172.31.0.1/32",mode="TRANSPORT",name="host-host",protocol="AH",remote_ts="172.31.0.2/32",reqid="X",uid="X"} 64 # HELP ipsec_child_sa_bytes_out Number of output bytes processed. # TYPE ipsec_child_sa_bytes_out gauge -ipsec_child_sa_bytes_out{ike_sa_local_host="172.31.0.1",ike_sa_local_id="moon",ike_sa_name="host-host",ike_sa_remote_host="172.31.0.2",ike_sa_remote_id="sun",ike_sa_remote_identity="",ike_sa_uid="1",ike_sa_version="2",ike_sa_vips="",local_ts="172.31.0.1/32",mode="TRANSPORT",name="host-host",protocol="AH",remote_ts="172.31.0.2/32",reqid="1",uid="1"} 64 +ipsec_child_sa_bytes_out{ike_sa_local_host="172.31.0.1",ike_sa_local_id="moon",ike_sa_name="host-host",ike_sa_remote_host="172.31.0.2",ike_sa_remote_id="sun",ike_sa_remote_identity="",ike_sa_uid="X",ike_sa_version="2",ike_sa_vips="",local_ts="172.31.0.1/32",mode="TRANSPORT",name="host-host",protocol="AH",remote_ts="172.31.0.2/32",reqid="X",uid="X"} 64 # HELP ipsec_child_sa_packets_in Number of input packets processed. # TYPE ipsec_child_sa_packets_in gauge -ipsec_child_sa_packets_in{ike_sa_local_host="172.31.0.1",ike_sa_local_id="moon",ike_sa_name="host-host",ike_sa_remote_host="172.31.0.2",ike_sa_remote_id="sun",ike_sa_remote_identity="",ike_sa_uid="1",ike_sa_version="2",ike_sa_vips="",local_ts="172.31.0.1/32",mode="TRANSPORT",name="host-host",protocol="AH",remote_ts="172.31.0.2/32",reqid="1",uid="1"} 1 +ipsec_child_sa_packets_in{ike_sa_local_host="172.31.0.1",ike_sa_local_id="moon",ike_sa_name="host-host",ike_sa_remote_host="172.31.0.2",ike_sa_remote_id="sun",ike_sa_remote_identity="",ike_sa_uid="X",ike_sa_version="2",ike_sa_vips="",local_ts="172.31.0.1/32",mode="TRANSPORT",name="host-host",protocol="AH",remote_ts="172.31.0.2/32",reqid="X",uid="X"} 1 # HELP ipsec_child_sa_packets_out Number of output packets processed. # TYPE ipsec_child_sa_packets_out gauge -ipsec_child_sa_packets_out{ike_sa_local_host="172.31.0.1",ike_sa_local_id="moon",ike_sa_name="host-host",ike_sa_remote_host="172.31.0.2",ike_sa_remote_id="sun",ike_sa_remote_identity="",ike_sa_uid="1",ike_sa_version="2",ike_sa_vips="",local_ts="172.31.0.1/32",mode="TRANSPORT",name="host-host",protocol="AH",remote_ts="172.31.0.2/32",reqid="1",uid="1"} 1 +ipsec_child_sa_packets_out{ike_sa_local_host="172.31.0.1",ike_sa_local_id="moon",ike_sa_name="host-host",ike_sa_remote_host="172.31.0.2",ike_sa_remote_id="sun",ike_sa_remote_identity="",ike_sa_uid="X",ike_sa_version="2",ike_sa_vips="",local_ts="172.31.0.1/32",mode="TRANSPORT",name="host-host",protocol="AH",remote_ts="172.31.0.2/32",reqid="X",uid="X"} 1 # HELP ipsec_child_sa_state Child SA state. # TYPE ipsec_child_sa_state gauge -ipsec_child_sa_state{ike_sa_local_host="172.31.0.1",ike_sa_local_id="moon",ike_sa_name="host-host",ike_sa_remote_host="172.31.0.2",ike_sa_remote_id="sun",ike_sa_remote_identity="",ike_sa_uid="1",ike_sa_version="2",ike_sa_vips="",local_ts="172.31.0.1/32",mode="TRANSPORT",name="host-host",protocol="AH",remote_ts="172.31.0.2/32",reqid="1",uid="1"} 3 +ipsec_child_sa_state{ike_sa_local_host="172.31.0.1",ike_sa_local_id="moon",ike_sa_name="host-host",ike_sa_remote_host="172.31.0.2",ike_sa_remote_id="sun",ike_sa_remote_identity="",ike_sa_uid="X",ike_sa_version="2",ike_sa_vips="",local_ts="172.31.0.1/32",mode="TRANSPORT",name="host-host",protocol="AH",remote_ts="172.31.0.2/32",reqid="X",uid="X"} 3 # HELP ipsec_half_open_ike_sas Number of IKE SAs in half-open state. # TYPE ipsec_half_open_ike_sas gauge ipsec_half_open_ike_sas 0 # HELP ipsec_ike_sa_state IKE SA state. # TYPE ipsec_ike_sa_state gauge -ipsec_ike_sa_state{local_host="172.31.0.1",local_id="moon",name="host-host",remote_host="172.31.0.2",remote_id="sun",remote_identity="",uid="1",version="2",vips=""} 2 +ipsec_ike_sa_state{local_host="172.31.0.1",local_id="moon",name="host-host",remote_host="172.31.0.2",remote_id="sun",remote_identity="",uid="X",version="2",vips=""} 2 # HELP ipsec_ike_sas Number of currently registered IKE SAs. # TYPE ipsec_ike_sas gauge ipsec_ike_sas 1 diff --git a/exporter/vici.go b/exporter/vici.go new file mode 100644 index 0000000..574852d --- /dev/null +++ b/exporter/vici.go @@ -0,0 +1,79 @@ +package exporter + +import ( + "net" + + "github.com/go-kit/kit/log/level" + "github.com/strongswan/govici/vici" +) + +func (e *Exporter) scrapeVICI() (m metrics, ok bool) { + e.mu.Lock() + defer e.mu.Unlock() + network, addr := e.address.Scheme, e.address.Host + if network == "unix" { + addr = e.address.Path + } + sess, err := vici.NewSession(vici.WithAddr(network, addr), vici.WithDialContext((&net.Dialer{Timeout: e.timeout}).DialContext)) + if err != nil { + level.Error(e.logger).Log("msg", "Failed to connect to charon", "err", err) + return + } + defer sess.Close() + + msg, err := sess.CommandRequest("stats", nil) + if err != nil { + level.Error(e.logger).Log("msg", "Failed to send command", "cmd", "stats", "err", err) + return + } + if msg.Err() != nil { + level.Error(e.logger).Log("msg", "Failed to process command response", "cmd", "stats", "err", err) + return + } + if err = vici.UnmarshalMessage(msg, &m.Stats); err != nil { + level.Error(e.logger).Log("msg", "Failed to unmarshal command response", "cmd", "stats", "err", err) + return + } + + msg, err = sess.CommandRequest("get-pools", nil) + if err != nil { + level.Error(e.logger).Log("msg", "Failed to send command", "cmd", "get-pools", "err", err) + return + } + if msg.Err() != nil { + level.Error(e.logger).Log("msg", "Failed to process command response", "cmd", "get-pools", "err", err) + return + } + pools := make(map[string]pool) + if err = vici.UnmarshalMessage(msg, pools); err != nil { + level.Error(e.logger).Log("msg", "Failed to unmarshal command response", "cmd", "get-pools", "err", err) + return + } + for name, pool := range pools { + pool.Name = name + m.Pools = append(m.Pools, pool) + } + + stream, err := sess.StreamedCommandRequest("list-sas", "list-sa", nil) + if err != nil { + level.Error(e.logger).Log("msg", "Failed to send command", "cmd", "list-sas", "err", err) + return + } + for _, msg := range stream.Messages() { + if msg.Err() != nil { + level.Error(e.logger).Log("msg", "Failed to process command response", "cmd", "list-sas", "err", err) + return + } + ikeSAs := make(map[string]ikeSA) + if err = vici.UnmarshalMessage(msg, ikeSAs); err != nil { + level.Error(e.logger).Log("msg", "Failed to unmarshal command response", "cmd", "list-sas", "err", err) + return + } + for name, ikeSA := range ikeSAs { + ikeSA.Name = name + m.IKESAs = append(m.IKESAs, &ikeSA) + } + } + ok = true + return +} diff --git a/go.mod b/go.mod index f0cb27b..dffe799 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/kr/pretty v0.3.0 // indirect github.com/prometheus/client_golang v1.11.0 + github.com/prometheus/client_model v0.2.0 github.com/prometheus/common v0.30.0 github.com/prometheus/exporter-toolkit v0.6.1 github.com/prometheus/procfs v0.7.1 // indirect diff --git a/go.sum b/go.sum index 8edc6de..e45a64c 100644 --- a/go.sum +++ b/go.sum @@ -43,7 +43,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 h1:AUNCr9CiJuwrRYS3XieqF+Z9B9gNxo/eANAJCF2eiN4= github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= @@ -156,7 +155,6 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -224,13 +222,11 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -306,14 +302,12 @@ github.com/prometheus/exporter-toolkit v0.6.1/go.mod h1:ZUBIj498ePooX9t/2xtDjeQY github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.1 h1:TlEtJq5GvGqMykEwWzbZWjjztF86swFhsPix1i0bkgA= github.com/prometheus/procfs v0.7.1/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= @@ -368,7 +362,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -439,7 +432,6 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -448,7 +440,6 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 h1:3B43BWw0xEBsLZ/NO1VALz6fppU3481pik+2Ksv45z8= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= @@ -513,7 +504,6 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -601,7 +591,6 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= @@ -669,7 +658,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/testdata/docker/libreswan/docker-compose.yml b/testdata/docker/libreswan/docker-compose.yml new file mode 100644 index 0000000..1c2b3fa --- /dev/null +++ b/testdata/docker/libreswan/docker-compose.yml @@ -0,0 +1,34 @@ +version: '2' +services: + moon: + build: + context: ./moon + cap_add: + - NET_ADMIN + privileged: true + container_name: libreswan-moon + networks: + libreswan: + ipv4_address: 172.31.1.1 + aliases: + - moon + + sun: + build: + context: ./sun + cap_add: + - NET_ADMIN + privileged: true + networks: + libreswan: + ipv4_address: 172.31.1.2 + aliases: + - sun + +networks: + libreswan: + driver: bridge + ipam: + config: + - subnet: 172.31.1.0/24 + gateway: 172.31.1.254 diff --git a/testdata/docker/libreswan/moon/Dockerfile b/testdata/docker/libreswan/moon/Dockerfile new file mode 100644 index 0000000..f996631 --- /dev/null +++ b/testdata/docker/libreswan/moon/Dockerfile @@ -0,0 +1,13 @@ +FROM debian:bullseye + +RUN set -x \ + && apt-get update \ + && apt-get install -y libreswan iputils-ping \ + && rm -rf /var/lib/apt/lists/* \ + && set +x + +RUN mkdir -p /run/pluto + +COPY rootfs / + +ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/testdata/docker/libreswan/moon/rootfs/docker-entrypoint.sh b/testdata/docker/libreswan/moon/rootfs/docker-entrypoint.sh new file mode 100755 index 0000000..cc6f0c9 --- /dev/null +++ b/testdata/docker/libreswan/moon/rootfs/docker-entrypoint.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +set -e + +rm -f /run/pluto/pluto.pid || true + +/usr/libexec/ipsec/_stackmanager start +/usr/sbin/ipsec --checknss +/usr/sbin/ipsec --checknflog +/usr/libexec/ipsec/pluto --config /etc/ipsec.conf +/usr/libexec/ipsec/addconn --config /etc/ipsec.conf --autoall + +sleep 2 +ping -c 1 sun + +exec sleep infinity diff --git a/testdata/docker/libreswan/moon/rootfs/etc/ipsec.conf b/testdata/docker/libreswan/moon/rootfs/etc/ipsec.conf new file mode 100644 index 0000000..079118d --- /dev/null +++ b/testdata/docker/libreswan/moon/rootfs/etc/ipsec.conf @@ -0,0 +1,12 @@ +config setup + logfile=/dev/stdout + +conn host-host + left=172.31.1.1 + right=172.31.1.2 + authby=secret + rekeymargin=5400 + ikev2=yes + mobike=no + phase2alg=aes256-aes_xcbc + auto=start diff --git a/testdata/docker/libreswan/moon/rootfs/etc/ipsec.secrets b/testdata/docker/libreswan/moon/rootfs/etc/ipsec.secrets new file mode 100644 index 0000000..4f04c77 --- /dev/null +++ b/testdata/docker/libreswan/moon/rootfs/etc/ipsec.secrets @@ -0,0 +1 @@ +172.31.1.1 172.31.1.2 : PSK "0sv+NkxY9LLZvwj4qCC2o/gGrWDF2d21jL" diff --git a/testdata/docker/libreswan/sun/Dockerfile b/testdata/docker/libreswan/sun/Dockerfile new file mode 100644 index 0000000..bfe1e88 --- /dev/null +++ b/testdata/docker/libreswan/sun/Dockerfile @@ -0,0 +1,13 @@ +FROM debian:bullseye + +RUN set -x \ + && apt-get update \ + && apt-get install -y libreswan \ + && rm -rf /var/lib/apt/lists/* \ + && set +x + +RUN mkdir -p /run/pluto + +COPY rootfs / + +ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/testdata/docker/libreswan/sun/rootfs/docker-entrypoint.sh b/testdata/docker/libreswan/sun/rootfs/docker-entrypoint.sh new file mode 100755 index 0000000..c15d6be --- /dev/null +++ b/testdata/docker/libreswan/sun/rootfs/docker-entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e + +rm -f /run/pluto/pluto.pid || true + +/usr/libexec/ipsec/_stackmanager start +/usr/sbin/ipsec --checknss +/usr/sbin/ipsec --checknflog +/usr/libexec/ipsec/pluto --config /etc/ipsec.conf +/usr/libexec/ipsec/addconn --config /etc/ipsec.conf --autoall + +exec sleep infinity diff --git a/testdata/docker/libreswan/sun/rootfs/etc/ipsec.conf b/testdata/docker/libreswan/sun/rootfs/etc/ipsec.conf new file mode 100644 index 0000000..1c99f85 --- /dev/null +++ b/testdata/docker/libreswan/sun/rootfs/etc/ipsec.conf @@ -0,0 +1,12 @@ +config setup + logfile=/dev/stdout + +conn host-host + left=172.31.1.2 + right=172.31.1.1 + authby=secret + rekeymargin=5400 + ikev2=yes + mobike=no + phase2alg=aes256-aes_xcbc + auto=add diff --git a/testdata/docker/libreswan/sun/rootfs/etc/ipsec.secrets b/testdata/docker/libreswan/sun/rootfs/etc/ipsec.secrets new file mode 100644 index 0000000..4f04c77 --- /dev/null +++ b/testdata/docker/libreswan/sun/rootfs/etc/ipsec.secrets @@ -0,0 +1 @@ +172.31.1.1 172.31.1.2 : PSK "0sv+NkxY9LLZvwj4qCC2o/gGrWDF2d21jL" diff --git a/testdata/docker/docker-compose.yml b/testdata/docker/strongswan/docker-compose.yml similarity index 94% rename from testdata/docker/docker-compose.yml rename to testdata/docker/strongswan/docker-compose.yml index 6585f12..cf524af 100644 --- a/testdata/docker/docker-compose.yml +++ b/testdata/docker/strongswan/docker-compose.yml @@ -6,7 +6,7 @@ services: cap_add: - NET_ADMIN privileged: true - container_name: moon + container_name: strongswan-moon networks: strongswan: ipv4_address: 172.31.0.1 diff --git a/testdata/docker/strongswan/moon/Dockerfile b/testdata/docker/strongswan/moon/Dockerfile new file mode 100644 index 0000000..13343a0 --- /dev/null +++ b/testdata/docker/strongswan/moon/Dockerfile @@ -0,0 +1,13 @@ +FROM debian:bullseye + +RUN set -x \ + && apt-get update \ + && apt-get install -y iputils-ping strongswan-charon strongswan-swanctl \ + && rm -rf /var/lib/apt/lists/* \ + && set +x + +COPY rootfs / + +EXPOSE 4502 + +CMD /usr/lib/ipsec/charon diff --git a/testdata/docker/moon/rootfs/etc/strongswan.d/charon-swanctl.conf b/testdata/docker/strongswan/moon/rootfs/etc/strongswan.d/charon-swanctl.conf similarity index 100% rename from testdata/docker/moon/rootfs/etc/strongswan.d/charon-swanctl.conf rename to testdata/docker/strongswan/moon/rootfs/etc/strongswan.d/charon-swanctl.conf diff --git a/testdata/docker/moon/rootfs/etc/strongswan.d/charon/vici.conf b/testdata/docker/strongswan/moon/rootfs/etc/strongswan.d/charon/vici.conf similarity index 100% rename from testdata/docker/moon/rootfs/etc/strongswan.d/charon/vici.conf rename to testdata/docker/strongswan/moon/rootfs/etc/strongswan.d/charon/vici.conf diff --git a/testdata/docker/moon/rootfs/etc/swanctl/swanctl.conf b/testdata/docker/strongswan/moon/rootfs/etc/swanctl/swanctl.conf similarity index 100% rename from testdata/docker/moon/rootfs/etc/swanctl/swanctl.conf rename to testdata/docker/strongswan/moon/rootfs/etc/swanctl/swanctl.conf diff --git a/testdata/docker/moon/Dockerfile b/testdata/docker/strongswan/sun/Dockerfile similarity index 69% rename from testdata/docker/moon/Dockerfile rename to testdata/docker/strongswan/sun/Dockerfile index 93cac9c..7d0cb0a 100644 --- a/testdata/docker/moon/Dockerfile +++ b/testdata/docker/strongswan/sun/Dockerfile @@ -1,13 +1,11 @@ -FROM debian:buster +FROM debian:bullseye -RUN set -x \ - && apt-get update \ +RUN set -x \ + && apt-get update \ && apt-get install -y strongswan-charon strongswan-swanctl \ && rm -rf /var/lib/apt/lists/* \ && set +x COPY rootfs / -EXPOSE 4502 - CMD /usr/lib/ipsec/charon diff --git a/testdata/docker/sun/rootfs/etc/strongswan.d/charon-swanctl.conf b/testdata/docker/strongswan/sun/rootfs/etc/strongswan.d/charon-swanctl.conf similarity index 100% rename from testdata/docker/sun/rootfs/etc/strongswan.d/charon-swanctl.conf rename to testdata/docker/strongswan/sun/rootfs/etc/strongswan.d/charon-swanctl.conf diff --git a/testdata/docker/sun/rootfs/etc/swanctl/swanctl.conf b/testdata/docker/strongswan/sun/rootfs/etc/swanctl/swanctl.conf similarity index 100% rename from testdata/docker/sun/rootfs/etc/swanctl/swanctl.conf rename to testdata/docker/strongswan/sun/rootfs/etc/swanctl/swanctl.conf diff --git a/testdata/docker/sun/Dockerfile b/testdata/docker/sun/Dockerfile deleted file mode 100644 index d28612b..0000000 --- a/testdata/docker/sun/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM debian:buster - -RUN set -x \ - && apt-get update \ - && apt-get install -y strongswan-charon strongswan-swanctl \ - && rm -rf /var/lib/apt/lists/* \ - && set +x - -COPY rootfs / - -CMD /usr/lib/ipsec/charon