Skip to content

Commit

Permalink
add ssl certificate metrics (#130)
Browse files Browse the repository at this point in the history
Co-authored-by: Peiman Jafari <[email protected]>
  • Loading branch information
cortesem and peimanja authored Feb 18, 2024
1 parent 6fc2d63 commit fcb76b4
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 4 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ Some metrics are not available with Artifactory OSS license. The exporter return
| artifactory_exporter_total_api_errors | Current total Artifactory API errors when scraping for stats. | | &#9989; |
| artifactory_exporter_json_parse_failures |Number of errors while parsing Json. | | &#9989; |
| artifactory_replication_enabled | Replication status for an Artifactory repository (1 = enabled). | `name`, `type`, `cron_exp`, `status` | |
| artifactory_security_certificates | SSL certificate name and expiry as labels, seconds to expiration as value | `alias`, `expires`, `issued_by` | |
| artifactory_security_groups | Number of Artifactory groups. | | |
| artifactory_security_users | Number of Artifactory users for each realm. | `realm` | |
| artifactory_storage_artifacts | Total artifacts count stored in Artifactory. | | &#9989; |
Expand Down
40 changes: 38 additions & 2 deletions artifactory/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
)

const (
usersEndpoint = "security/users"
groupsEndpoint = "security/groups"
usersEndpoint = "security/users"
groupsEndpoint = "security/groups"
certificatesEndpoint = "system/security/certificates"
)

// User represents single element of API respond from users endpoint
Expand Down Expand Up @@ -67,3 +68,38 @@ func (c *Client) FetchGroups() (Groups, error) {

return groups, nil
}

// Certificate represents a single element of an API response from the certificates endpoint
type Certificate struct {
CertificateAlias string `json:"certificateAlias"`
IssuedTo string `json:"issuedTo"`
IssuedBy string `json:"issuedBy"`
IssuedOn string `json:"issuedOn"`
ValidUntil string `json:"validUntil"`
Fingerprint string `json:"fingerprint"`
}

type Certificates struct {
Certificates []Certificate
NodeId string
}

// FetchCertificates makes the API call to the certificates endpoint and returns []Certificates
func (c *Client) FetchCertificates() (Certificates, error) {
var certs Certificates
c.logger.Debug("Fetching certificate stats")
resp, err := c.FetchHTTP(certificatesEndpoint)
if err != nil {
return certs, err
}
certs.NodeId = resp.NodeId
if err := json.Unmarshal(resp.Body, &certs.Certificates); err != nil {
c.logger.Error("There was an issue when try to unmarshal certificates response")
return certs, &UnmarshalError{
message: err.Error(),
endpoint: certificatesEndpoint,
}
}

return certs, nil
}
11 changes: 9 additions & 2 deletions collector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var (
repoLabelNames = append([]string{"name", "type", "package_type"}, defaultLabelNames...)
replicationLabelNames = append([]string{"name", "type", "url", "cron_exp", "status"}, defaultLabelNames...)
federationLabelNames = append([]string{"name", "remote_url", "remote_name"}, defaultLabelNames...)
certificateLabelNames = append([]string{"alias", "issued_by", "expires"}, defaultLabelNames...)
)

func newMetric(metricName string, subsystem string, docString string, labelNames []string) *prometheus.Desc {
Expand All @@ -31,8 +32,9 @@ var (
}

securityMetrics = metrics{
"users": newMetric("users", "security", "Number of Artifactory users for each realm.", append([]string{"realm"}, defaultLabelNames...)),
"groups": newMetric("groups", "security", "Number of Artifactory groups", defaultLabelNames),
"users": newMetric("users", "security", "Number of Artifactory users for each realm.", append([]string{"realm"}, defaultLabelNames...)),
"groups": newMetric("groups", "security", "Number of Artifactory groups", defaultLabelNames),
"certificates": newMetric("certificates", "security", "Internal SSL certificate information, seconds to expiration as value", certificateLabelNames),
}

storageMetrics = metrics{
Expand Down Expand Up @@ -154,6 +156,11 @@ func (e *Exporter) scrape(ch chan<- prometheus.Metric) (up float64) {
if err != nil {
return 0
}
case "certificates":
err := e.exportCertificates(metricName, metric, ch)
if err != nil {
return 0
}
}
}
err = e.exportReplications(ch)
Expand Down
46 changes: 46 additions & 0 deletions collector/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package collector

import (
"fmt"
"time"

"github.com/prometheus/client_golang/prometheus"

Expand Down Expand Up @@ -85,3 +86,48 @@ func (e *Exporter) exportGroups(metricName string, metric *prometheus.Desc, ch c
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, float64(len(groups.Groups)), groups.NodeId)
return nil
}

func (e *Exporter) exportCertificates(metricName string, metric *prometheus.Desc, ch chan<- prometheus.Metric) error {
// Fetch Artifactory certificates
certs, err := e.client.FetchCertificates()
if err != nil {
e.logger.Error(
"Couldn't scrape Artifactory when fetching system/security/certificates",
"err", err.Error(),
)
e.totalAPIErrors.Inc()
return err
}
if len(certs.Certificates) == 0 {
e.logger.Debug("No certificates found")
return nil
}

for _, certificate := range certs.Certificates {
var validThrough float64
timeNow := float64(time.Now().Unix())
if validThroughTime, err := time.Parse(time.RFC3339, certificate.ValidUntil); err != nil {
e.logger.Warn(
"Couldn't parse certificate ValidThrough",
"err", err.Error(),
)
validThrough = timeNow
} else {
validThrough = float64(validThroughTime.Unix())
}

alias := certificate.CertificateAlias
issued_by := certificate.IssuedBy
valid_until := certificate.ValidUntil
e.logger.Debug(
"Registering metric",
"metric", metricName,
"alias", alias,
"issued_by", issued_by,
"valid_until", valid_until,
)
ch <- prometheus.MustNewConstMetric(metric, prometheus.GaugeValue, validThrough-timeNow, alias, issued_by, valid_until, certs.NodeId)
}

return nil
}

0 comments on commit fcb76b4

Please sign in to comment.