Skip to content

Commit a3327c4

Browse files
authored
Add TLS certs expiration metric
1 parent 3140a4e commit a3327c4

File tree

15 files changed

+291
-62
lines changed

15 files changed

+291
-62
lines changed

cmd/traefik/traefik.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"context"
5+
"crypto/x509"
56
"encoding/json"
67
stdlog "log"
78
"net/http"
@@ -14,6 +15,7 @@ import (
1415
"github.com/coreos/go-systemd/daemon"
1516
assetfs "github.com/elazarl/go-bindata-assetfs"
1617
"github.com/go-acme/lego/v4/challenge"
18+
gokitmetrics "github.com/go-kit/kit/metrics"
1719
"github.com/sirupsen/logrus"
1820
"github.com/traefik/paerser/cli"
1921
"github.com/traefik/traefik/v2/autogen/genstatic"
@@ -260,6 +262,11 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
260262
watcher.AddListener(func(conf dynamic.Configuration) {
261263
ctx := context.Background()
262264
tlsManager.UpdateConfigs(ctx, conf.TLS.Stores, conf.TLS.Options, conf.TLS.Certificates)
265+
266+
gauge := metricsRegistry.TLSCertsNotAfterTimestampGauge()
267+
for _, certificate := range tlsManager.GetCertificates() {
268+
appendCertMetric(gauge, certificate)
269+
}
263270
})
264271

265272
// Metrics
@@ -432,6 +439,20 @@ func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry {
432439
return registries
433440
}
434441

442+
func appendCertMetric(gauge gokitmetrics.Gauge, certificate *x509.Certificate) {
443+
sort.Strings(certificate.DNSNames)
444+
445+
labels := []string{
446+
"cn", certificate.Subject.CommonName,
447+
"serial", certificate.SerialNumber.String(),
448+
"sans", strings.Join(certificate.DNSNames, ","),
449+
}
450+
451+
notAfter := float64(certificate.NotAfter.Unix())
452+
453+
gauge.With(labels...).Set(notAfter)
454+
}
455+
435456
func setupAccessLog(conf *types.AccessLog) *accesslog.Handler {
436457
if conf == nil {
437458
return nil

cmd/traefik/traefik_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package main
2+
3+
import (
4+
"crypto/x509"
5+
"encoding/pem"
6+
"strings"
7+
"testing"
8+
9+
"github.com/go-kit/kit/metrics"
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
// FooCert is a PEM-encoded TLS cert.
15+
// generated from src/crypto/tls:
16+
// go run generate_cert.go --rsa-bits 1024 --host foo.org,foo.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
17+
const fooCert = `-----BEGIN CERTIFICATE-----
18+
MIICHzCCAYigAwIBAgIQXQFLeYRwc5X21t457t2xADANBgkqhkiG9w0BAQsFADAS
19+
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
20+
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
21+
iQKBgQDCjn67GSs/khuGC4GNN+tVo1S+/eSHwr/hWzhfMqO7nYiXkFzmxi+u14CU
22+
Pda6WOeps7T2/oQEFMxKKg7zYOqkLSbjbE0ZfosopaTvEsZm/AZHAAvoOrAsIJOn
23+
SEiwy8h0tLA4z1SNR6rmIVQWyqBZEPAhBTQM1z7tFp48FakCFwIDAQABo3QwcjAO
24+
BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw
25+
AwEB/zAdBgNVHQ4EFgQUDHG3ASzeUezElup9zbPpBn/vjogwGwYDVR0RBBQwEoIH
26+
Zm9vLm9yZ4IHZm9vLmNvbTANBgkqhkiG9w0BAQsFAAOBgQBT+VLMbB9u27tBX8Aw
27+
ZrGY3rbNdBGhXVTksrjiF+6ZtDpD3iI56GH9zLxnqvXkgn3u0+Ard5TqF/xmdwVw
28+
NY0V/aWYfcL2G2auBCQrPvM03ozRnVUwVfP23eUzX2ORNHCYhd2ObQx4krrhs7cJ
29+
SWxtKwFlstoXY3K2g9oRD9UxdQ==
30+
-----END CERTIFICATE-----`
31+
32+
// BarCert is a PEM-encoded TLS cert.
33+
// generated from src/crypto/tls:
34+
// go run generate_cert.go --rsa-bits 1024 --host bar.org,bar.com --ca --start-date "Jan 1 00:00:00 1970" --duration=10000h
35+
const barCert = `-----BEGIN CERTIFICATE-----
36+
MIICHTCCAYagAwIBAgIQcuIcNEXzBHPoxna5S6wG4jANBgkqhkiG9w0BAQsFADAS
37+
MRAwDgYDVQQKEwdBY21lIENvMB4XDTcwMDEwMTAwMDAwMFoXDTcxMDIyMTE2MDAw
38+
MFowEjEQMA4GA1UEChMHQWNtZSBDbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
39+
gYEAqtcrP+KA7D6NjyztGNIPMup9KiBMJ8QL+preog/YHR7SQLO3kGFhpS3WKMab
40+
SzMypC3ZX1PZjBP5ZzwaV3PFbuwlCkPlyxR2lOWmullgI7mjY0TBeYLDIclIzGRp
41+
mpSDDSpkW1ay2iJDSpXjlhmwZr84hrCU7BRTQJo91fdsRTsCAwEAAaN0MHIwDgYD
42+
VR0PAQH/BAQDAgKkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMB
43+
Af8wHQYDVR0OBBYEFK8jnzFQvBAgWtfzOyXY4VSkwrTXMBsGA1UdEQQUMBKCB2Jh
44+
ci5vcmeCB2Jhci5jb20wDQYJKoZIhvcNAQELBQADgYEAJz0ifAExisC/ZSRhWuHz
45+
7qs1i6Nd4+YgEVR8dR71MChP+AMxucY1/ajVjb9xlLys3GPE90TWSdVppabEVjZY
46+
Oq11nPKc50ItTt8dMku6t0JHBmzoGdkN0V4zJCBqdQJxhop8JpYJ0S9CW0eT93h3
47+
ipYQSsmIINGtMXJ8VkP/MlM=
48+
-----END CERTIFICATE-----`
49+
50+
type gaugeMock struct {
51+
metrics map[string]float64
52+
labels string
53+
}
54+
55+
func (g gaugeMock) With(labelValues ...string) metrics.Gauge {
56+
g.labels = strings.Join(labelValues, ",")
57+
return g
58+
}
59+
60+
func (g gaugeMock) Set(value float64) {
61+
g.metrics[g.labels] = value
62+
}
63+
64+
func (g gaugeMock) Add(delta float64) {
65+
panic("implement me")
66+
}
67+
68+
func TestAppendCertMetric(t *testing.T) {
69+
testCases := []struct {
70+
desc string
71+
certs []string
72+
expected map[string]float64
73+
}{
74+
{
75+
desc: "No certs",
76+
certs: []string{},
77+
expected: map[string]float64{},
78+
},
79+
{
80+
desc: "One cert",
81+
certs: []string{fooCert},
82+
expected: map[string]float64{
83+
"cn,,serial,123624926713171615935660664614975025408,sans,foo.com,foo.org": 3.6e+09,
84+
},
85+
},
86+
{
87+
desc: "Two certs",
88+
certs: []string{fooCert, barCert},
89+
expected: map[string]float64{
90+
"cn,,serial,123624926713171615935660664614975025408,sans,foo.com,foo.org": 3.6e+09,
91+
"cn,,serial,152706022658490889223053211416725817058,sans,bar.com,bar.org": 3.6e+07,
92+
},
93+
},
94+
}
95+
96+
for _, test := range testCases {
97+
test := test
98+
t.Run(test.desc, func(t *testing.T) {
99+
t.Parallel()
100+
101+
gauge := &gaugeMock{
102+
metrics: map[string]float64{},
103+
}
104+
105+
for _, cert := range test.certs {
106+
block, _ := pem.Decode([]byte(cert))
107+
parsedCert, err := x509.ParseCertificate(block.Bytes)
108+
require.NoError(t, err)
109+
110+
appendCertMetric(gauge, parsedCert)
111+
}
112+
113+
assert.Equal(t, test.expected, gauge.metrics)
114+
})
115+
}
116+
}

docs/content/providers/docker.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ See the list of labels in the dedicated [routing](../routing/providers/docker.md
9898
By default, Traefik watches for [container level labels](https://docs.docker.com/config/labels-custom-metadata/) on a standalone Docker Engine.
9999

100100
When using Docker Compose, labels are specified by the directive
101-
[`labels`](https://docs.docker.com/compose/compose-file/#labels) from the
102-
["services" objects](https://docs.docker.com/compose/compose-file/#service-configuration-reference).
101+
[`labels`](https://docs.docker.com/compose/compose-file/compose-file-v3/#labels) from the
102+
["services" objects](https://docs.docker.com/compose/compose-file/compose-file-v3/#service-configuration-reference).
103103

104104
!!! tip "Not Only Docker"
105105
Please note that any tool like Nomad, Terraform, Ansible, etc.
@@ -186,7 +186,7 @@ set the [`swarmMode`](#swarmmode) directive to `true`.
186186
While in Swarm Mode, Traefik uses labels found on services, not on individual containers.
187187

188188
Therefore, if you use a compose file with Swarm Mode, labels should be defined in the
189-
[`deploy`](https://docs.docker.com/compose/compose-file/#labels-1) part of your service.
189+
[`deploy`](https://docs.docker.com/compose/compose-file/compose-file-v3/#labels-1) part of your service.
190190

191191
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file)).
192192

docs/content/routing/providers/docker.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ Attach labels to your containers and let Traefik do the rest!
124124
!!! important "Labels in Docker Swarm Mode"
125125
While in Swarm Mode, Traefik uses labels found on services, not on individual containers.
126126
Therefore, if you use a compose file with Swarm Mode, labels should be defined in the `deploy` part of your service.
127-
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file/#labels-1)).
127+
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file/compose-file-v3/#labels-1)).
128128

129129
## Routing Configuration
130130

pkg/metrics/datadog.go

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,19 @@ var datadogTicker *time.Ticker
2020

2121
// Metric names consistent with https://github.com/DataDog/integrations-extras/pull/64
2222
const (
23-
ddMetricsServiceReqsName = "service.request.total"
24-
ddMetricsServiceLatencyName = "service.request.duration"
25-
ddRetriesTotalName = "service.retries.total"
26-
ddConfigReloadsName = "config.reload.total"
27-
ddConfigReloadsFailureTagName = "failure"
28-
ddLastConfigReloadSuccessName = "config.reload.lastSuccessTimestamp"
29-
ddLastConfigReloadFailureName = "config.reload.lastFailureTimestamp"
30-
ddEntryPointReqsName = "entrypoint.request.total"
31-
ddEntryPointReqDurationName = "entrypoint.request.duration"
32-
ddEntryPointOpenConnsName = "entrypoint.connections.open"
33-
ddOpenConnsName = "service.connections.open"
34-
ddServerUpName = "service.server.up"
23+
ddMetricsServiceReqsName = "service.request.total"
24+
ddMetricsServiceLatencyName = "service.request.duration"
25+
ddRetriesTotalName = "service.retries.total"
26+
ddConfigReloadsName = "config.reload.total"
27+
ddConfigReloadsFailureTagName = "failure"
28+
ddLastConfigReloadSuccessName = "config.reload.lastSuccessTimestamp"
29+
ddLastConfigReloadFailureName = "config.reload.lastFailureTimestamp"
30+
ddEntryPointReqsName = "entrypoint.request.total"
31+
ddEntryPointReqDurationName = "entrypoint.request.duration"
32+
ddEntryPointOpenConnsName = "entrypoint.connections.open"
33+
ddOpenConnsName = "service.connections.open"
34+
ddServerUpName = "service.server.up"
35+
ddTLSCertsNotAfterTimestampName = "tls.certs.notAfterTimestamp"
3536
)
3637

3738
// RegisterDatadog registers the metrics pusher if this didn't happen yet and creates a datadog Registry instance.
@@ -41,10 +42,11 @@ func RegisterDatadog(ctx context.Context, config *types.Datadog) Registry {
4142
}
4243

4344
registry := &standardRegistry{
44-
configReloadsCounter: datadogClient.NewCounter(ddConfigReloadsName, 1.0),
45-
configReloadsFailureCounter: datadogClient.NewCounter(ddConfigReloadsName, 1.0).With(ddConfigReloadsFailureTagName, "true"),
46-
lastConfigReloadSuccessGauge: datadogClient.NewGauge(ddLastConfigReloadSuccessName),
47-
lastConfigReloadFailureGauge: datadogClient.NewGauge(ddLastConfigReloadFailureName),
45+
configReloadsCounter: datadogClient.NewCounter(ddConfigReloadsName, 1.0),
46+
configReloadsFailureCounter: datadogClient.NewCounter(ddConfigReloadsName, 1.0).With(ddConfigReloadsFailureTagName, "true"),
47+
lastConfigReloadSuccessGauge: datadogClient.NewGauge(ddLastConfigReloadSuccessName),
48+
lastConfigReloadFailureGauge: datadogClient.NewGauge(ddLastConfigReloadFailureName),
49+
tlsCertsNotAfterTimestampGauge: datadogClient.NewGauge(ddTLSCertsNotAfterTimestampName),
4850
}
4951

5052
if config.AddEntryPointsLabels {

pkg/metrics/datadog_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func TestDatadog(t *testing.T) {
3636
"traefik.entrypoint.request.duration:10000.000000|h|#entrypoint:test\n",
3737
"traefik.entrypoint.connections.open:1.000000|g|#entrypoint:test\n",
3838
"traefik.service.server.up:1.000000|g|#service:test,url:http://127.0.0.1,one:two\n",
39+
"traefik.tls.certs.notAfterTimestamp:1.000000|g|#key:value\n",
3940
}
4041

4142
udp.ShouldReceiveAll(t, expected, func() {
@@ -50,5 +51,6 @@ func TestDatadog(t *testing.T) {
5051
datadogRegistry.EntryPointReqDurationHistogram().With("entrypoint", "test").Observe(10000)
5152
datadogRegistry.EntryPointOpenConnsGauge().With("entrypoint", "test").Set(1)
5253
datadogRegistry.ServiceServerUpGauge().With("service", "test", "url", "http://127.0.0.1", "one", "two").Set(1)
54+
datadogRegistry.TLSCertsNotAfterTimestampGauge().With("key", "value").Set(1)
5355
})
5456
}

pkg/metrics/influxdb.go

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,19 @@ type influxDBWriter struct {
2626
var influxDBTicker *time.Ticker
2727

2828
const (
29-
influxDBMetricsServiceReqsName = "traefik.service.requests.total"
30-
influxDBMetricsServiceLatencyName = "traefik.service.request.duration"
31-
influxDBRetriesTotalName = "traefik.service.retries.total"
32-
influxDBConfigReloadsName = "traefik.config.reload.total"
33-
influxDBConfigReloadsFailureName = influxDBConfigReloadsName + ".failure"
34-
influxDBLastConfigReloadSuccessName = "traefik.config.reload.lastSuccessTimestamp"
35-
influxDBLastConfigReloadFailureName = "traefik.config.reload.lastFailureTimestamp"
36-
influxDBEntryPointReqsName = "traefik.entrypoint.requests.total"
37-
influxDBEntryPointReqDurationName = "traefik.entrypoint.request.duration"
38-
influxDBEntryPointOpenConnsName = "traefik.entrypoint.connections.open"
39-
influxDBOpenConnsName = "traefik.service.connections.open"
40-
influxDBServerUpName = "traefik.service.server.up"
29+
influxDBMetricsServiceReqsName = "traefik.service.requests.total"
30+
influxDBMetricsServiceLatencyName = "traefik.service.request.duration"
31+
influxDBRetriesTotalName = "traefik.service.retries.total"
32+
influxDBConfigReloadsName = "traefik.config.reload.total"
33+
influxDBConfigReloadsFailureName = influxDBConfigReloadsName + ".failure"
34+
influxDBLastConfigReloadSuccessName = "traefik.config.reload.lastSuccessTimestamp"
35+
influxDBLastConfigReloadFailureName = "traefik.config.reload.lastFailureTimestamp"
36+
influxDBEntryPointReqsName = "traefik.entrypoint.requests.total"
37+
influxDBEntryPointReqDurationName = "traefik.entrypoint.request.duration"
38+
influxDBEntryPointOpenConnsName = "traefik.entrypoint.connections.open"
39+
influxDBOpenConnsName = "traefik.service.connections.open"
40+
influxDBServerUpName = "traefik.service.server.up"
41+
influxDBTLSCertsNotAfterTimestampName = "traefik.tls.certs.notAfterTimestamp"
4142
)
4243

4344
const (
@@ -55,10 +56,11 @@ func RegisterInfluxDB(ctx context.Context, config *types.InfluxDB) Registry {
5556
}
5657

5758
registry := &standardRegistry{
58-
configReloadsCounter: influxDBClient.NewCounter(influxDBConfigReloadsName),
59-
configReloadsFailureCounter: influxDBClient.NewCounter(influxDBConfigReloadsFailureName),
60-
lastConfigReloadSuccessGauge: influxDBClient.NewGauge(influxDBLastConfigReloadSuccessName),
61-
lastConfigReloadFailureGauge: influxDBClient.NewGauge(influxDBLastConfigReloadFailureName),
59+
configReloadsCounter: influxDBClient.NewCounter(influxDBConfigReloadsName),
60+
configReloadsFailureCounter: influxDBClient.NewCounter(influxDBConfigReloadsFailureName),
61+
lastConfigReloadSuccessGauge: influxDBClient.NewGauge(influxDBLastConfigReloadSuccessName),
62+
lastConfigReloadFailureGauge: influxDBClient.NewGauge(influxDBLastConfigReloadFailureName),
63+
tlsCertsNotAfterTimestampGauge: influxDBClient.NewGauge(influxDBTLSCertsNotAfterTimestampName),
6264
}
6365

6466
if config.AddEntryPointsLabels {

pkg/metrics/influxdb_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,16 @@ func TestInfluxDB(t *testing.T) {
6464
})
6565

6666
assertMessage(t, msgEntrypoint, expectedEntrypoint)
67+
68+
expectedTLS := []string{
69+
`(traefik\.tls\.certs\.notAfterTimestamp,key=value value=1) [\d]{19}`,
70+
}
71+
72+
msgTLS := udp.ReceiveString(t, func() {
73+
influxDBRegistry.TLSCertsNotAfterTimestampGauge().With("key", "value").Set(1)
74+
})
75+
76+
assertMessage(t, msgTLS, expectedTLS)
6777
}
6878

6979
func TestInfluxDBHTTP(t *testing.T) {
@@ -121,6 +131,15 @@ func TestInfluxDBHTTP(t *testing.T) {
121131
msgEntrypoint := <-c
122132

123133
assertMessage(t, *msgEntrypoint, expectedEntrypoint)
134+
135+
expectedTLS := []string{
136+
`(traefik\.tls\.certs\.notAfterTimestamp,key=value value=1) [\d]{19}`,
137+
}
138+
139+
influxDBRegistry.TLSCertsNotAfterTimestampGauge().With("key", "value").Set(1)
140+
msgTLS := <-c
141+
142+
assertMessage(t, *msgTLS, expectedTLS)
124143
}
125144

126145
func assertMessage(t *testing.T, msg string, patterns []string) {

pkg/metrics/metrics.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ type Registry interface {
2121
LastConfigReloadSuccessGauge() metrics.Gauge
2222
LastConfigReloadFailureGauge() metrics.Gauge
2323

24+
// TLS
25+
TLSCertsNotAfterTimestampGauge() metrics.Gauge
26+
2427
// entry point metrics
2528
EntryPointReqsCounter() metrics.Counter
2629
EntryPointReqsTLSCounter() metrics.Counter
@@ -50,6 +53,7 @@ func NewMultiRegistry(registries []Registry) Registry {
5053
var configReloadsFailureCounter []metrics.Counter
5154
var lastConfigReloadSuccessGauge []metrics.Gauge
5255
var lastConfigReloadFailureGauge []metrics.Gauge
56+
var tlsCertsNotAfterTimestampGauge []metrics.Gauge
5357
var entryPointReqsCounter []metrics.Counter
5458
var entryPointReqsTLSCounter []metrics.Counter
5559
var entryPointReqDurationHistogram []ScalableHistogram
@@ -74,6 +78,9 @@ func NewMultiRegistry(registries []Registry) Registry {
7478
if r.LastConfigReloadFailureGauge() != nil {
7579
lastConfigReloadFailureGauge = append(lastConfigReloadFailureGauge, r.LastConfigReloadFailureGauge())
7680
}
81+
if r.TLSCertsNotAfterTimestampGauge() != nil {
82+
tlsCertsNotAfterTimestampGauge = append(tlsCertsNotAfterTimestampGauge, r.TLSCertsNotAfterTimestampGauge())
83+
}
7784
if r.EntryPointReqsCounter() != nil {
7885
entryPointReqsCounter = append(entryPointReqsCounter, r.EntryPointReqsCounter())
7986
}
@@ -113,6 +120,7 @@ func NewMultiRegistry(registries []Registry) Registry {
113120
configReloadsFailureCounter: multi.NewCounter(configReloadsFailureCounter...),
114121
lastConfigReloadSuccessGauge: multi.NewGauge(lastConfigReloadSuccessGauge...),
115122
lastConfigReloadFailureGauge: multi.NewGauge(lastConfigReloadFailureGauge...),
123+
tlsCertsNotAfterTimestampGauge: multi.NewGauge(tlsCertsNotAfterTimestampGauge...),
116124
entryPointReqsCounter: multi.NewCounter(entryPointReqsCounter...),
117125
entryPointReqsTLSCounter: multi.NewCounter(entryPointReqsTLSCounter...),
118126
entryPointReqDurationHistogram: NewMultiHistogram(entryPointReqDurationHistogram...),
@@ -133,6 +141,7 @@ type standardRegistry struct {
133141
configReloadsFailureCounter metrics.Counter
134142
lastConfigReloadSuccessGauge metrics.Gauge
135143
lastConfigReloadFailureGauge metrics.Gauge
144+
tlsCertsNotAfterTimestampGauge metrics.Gauge
136145
entryPointReqsCounter metrics.Counter
137146
entryPointReqsTLSCounter metrics.Counter
138147
entryPointReqDurationHistogram ScalableHistogram
@@ -169,6 +178,10 @@ func (r *standardRegistry) LastConfigReloadFailureGauge() metrics.Gauge {
169178
return r.lastConfigReloadFailureGauge
170179
}
171180

181+
func (r *standardRegistry) TLSCertsNotAfterTimestampGauge() metrics.Gauge {
182+
return r.tlsCertsNotAfterTimestampGauge
183+
}
184+
172185
func (r *standardRegistry) EntryPointReqsCounter() metrics.Counter {
173186
return r.entryPointReqsCounter
174187
}

0 commit comments

Comments
 (0)