Skip to content

Commit

Permalink
[receiver/tlscheck] Inital Commit of TLS Check Receiver (#35441)
Browse files Browse the repository at this point in the history
**Description:** Initial commit of TLS Check Receiver, a new component
aimed at emitting metrics about the expoiry of x.509 certs.

**Link to tracking Issue:**
[35423](#35423)

**Testing:** Test files added

**Documentation:** `README.md` & `metadata.yaml` added
  • Loading branch information
michael-burt authored Oct 9, 2024
1 parent e3c4b36 commit a1d7a33
Show file tree
Hide file tree
Showing 30 changed files with 1,420 additions and 0 deletions.
27 changes: 27 additions & 0 deletions .chloggen/tlscheckreceiver-addition.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: new_component

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: tlscheckreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add TLS Check Receiver component to monitor x.509 certificate expiry

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [35423]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [user]
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ receiver/sshcheckreceiver/ @open-teleme
receiver/statsdreceiver/ @open-telemetry/collector-contrib-approvers @jmacd @dmitryax
receiver/syslogreceiver/ @open-telemetry/collector-contrib-approvers @djaglowski @andrzej-stencel
receiver/tcplogreceiver/ @open-telemetry/collector-contrib-approvers @djaglowski
receiver/tlscheckreceiver/ @open-telemetry/collector-contrib-approvers @atoulme @michael-burt
receiver/udplogreceiver/ @open-telemetry/collector-contrib-approvers @djaglowski
receiver/vcenterreceiver/ @open-telemetry/collector-contrib-approvers @djaglowski @schmikei @StefanKurek
receiver/wavefrontreceiver/ @open-telemetry/collector-contrib-approvers @samiura
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ body:
- receiver/statsd
- receiver/syslog
- receiver/tcplog
- receiver/tlscheck
- receiver/udplog
- receiver/vcenter
- receiver/wavefront
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ body:
- receiver/statsd
- receiver/syslog
- receiver/tcplog
- receiver/tlscheck
- receiver/udplog
- receiver/vcenter
- receiver/wavefront
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/other.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ body:
- receiver/statsd
- receiver/syslog
- receiver/tcplog
- receiver/tlscheck
- receiver/udplog
- receiver/vcenter
- receiver/wavefront
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/unmaintained.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ body:
- receiver/statsd
- receiver/syslog
- receiver/tcplog
- receiver/tlscheck
- receiver/udplog
- receiver/vcenter
- receiver/wavefront
Expand Down
1 change: 1 addition & 0 deletions receiver/tlscheckreceiver/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ../../Makefile.Common
37 changes: 37 additions & 0 deletions receiver/tlscheckreceiver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# TLS Check Receiver

<!-- status autogenerated section -->
| Status | |
| ------------- |-----------|
| Stability | [development]: metrics |
| Distributions | [contrib] |
| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Areceiver%2Ftlscheck%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Areceiver%2Ftlscheck) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Areceiver%2Ftlscheck%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Areceiver%2Ftlscheck) |
| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@atoulme](https://www.github.com/atoulme), [@michael-burt](https://www.github.com/michael-burt) |

[development]: https://github.com/open-telemetry/opentelemetry-collector#development
[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib
<!-- end autogenerated section -->

Emit metrics about x.509 certificates.

## Getting Started

By default, the TLS Check Receiver will emit a single metric, `tlscheck.time_left`, per target. This is measured in seconds until the date and time specified in the `NotAfter` field of the x.509 certificate. After certificate expiration, the metric value will be a negative integer measuring the time in seconds since expiry.

## Example Configuration

```yaml
receivers:
tlscheck:
targets:
- url: https://example.com
- url: https://foobar.com:8080
```
## Certificate Verification
This component does not provide hostname, validity period, path, or CRL / OCSP verification on the certificate.
## Metrics
Details about the metrics produced by this receiver can be found in [metadata.yaml](./metadata.yaml).
63 changes: 63 additions & 0 deletions receiver/tlscheckreceiver/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package tlscheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver"

import (
"errors"
"fmt"
"net/url"

"go.opentelemetry.io/collector/receiver/scraperhelper"
"go.uber.org/multierr"

"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver/internal/metadata"
)

// Predefined error responses for configuration validation failures
var (
errMissingURL = errors.New(`"url" must be specified`)
errInvalidURL = errors.New(`"url" must be in the form of <scheme>://<hostname>[:<port>]`)
)

// Config defines the configuration for the various elements of the receiver agent.
type Config struct {
scraperhelper.ControllerConfig `mapstructure:",squash"`
metadata.MetricsBuilderConfig `mapstructure:",squash"`
Targets []*targetConfig `mapstructure:"targets"`
}

type targetConfig struct {
URL string `mapstructure:"url"`
}

// Validate validates the configuration by checking for missing or invalid fields
func (cfg *targetConfig) Validate() error {
var err error

if cfg.URL == "" {
err = multierr.Append(err, errMissingURL)
} else {
_, parseErr := url.ParseRequestURI(cfg.URL)
if parseErr != nil {
err = multierr.Append(err, fmt.Errorf("%s: %w", errInvalidURL.Error(), parseErr))
}
}

return err
}

// Validate validates the configuration by checking for missing or invalid fields
func (cfg *Config) Validate() error {
var err error

if len(cfg.Targets) == 0 {
err = multierr.Append(err, errMissingURL)
}

for _, target := range cfg.Targets {
err = multierr.Append(err, target.Validate())
}

return err
}
95 changes: 95 additions & 0 deletions receiver/tlscheckreceiver/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package tlscheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver"

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/receiver/scraperhelper"
)

func TestValidate(t *testing.T) {
testCases := []struct {
desc string
cfg *Config
expectedErr error
}{
{
desc: "missing url",
cfg: &Config{
Targets: []*targetConfig{},
ControllerConfig: scraperhelper.NewDefaultControllerConfig(),
},
expectedErr: errMissingURL,
},
{
desc: "invalid url",
cfg: &Config{
Targets: []*targetConfig{
{
URL: "invalid://endpoint: 12efg",
},
},
ControllerConfig: scraperhelper.NewDefaultControllerConfig(),
},
expectedErr: fmt.Errorf("%w: %s", errInvalidURL, `parse "invalid://endpoint: 12efg": invalid port ": 12efg" after host`),
},
{
desc: "invalid config with multiple targets",
cfg: &Config{
Targets: []*targetConfig{
{
URL: "invalid://endpoint: 12efg",
},
{
URL: "https://example.com",
},
},
ControllerConfig: scraperhelper.NewDefaultControllerConfig(),
},
expectedErr: fmt.Errorf("%w: %s", errInvalidURL, `parse "invalid://endpoint: 12efg": invalid port ": 12efg" after host`),
},
{
desc: "missing scheme",
cfg: &Config{
Targets: []*targetConfig{
{
URL: "www.opentelemetry.io/docs",
},
},
ControllerConfig: scraperhelper.NewDefaultControllerConfig(),
},
expectedErr: fmt.Errorf("%w: %s", errInvalidURL, `parse "www.opentelemetry.io/docs": invalid URI for request`),
},
{
desc: "valid config",
cfg: &Config{
Targets: []*targetConfig{
{
URL: "https://opentelemetry.io",
},
{
URL: "https://opentelemetry.io:80/docs",
},
},
ControllerConfig: scraperhelper.NewDefaultControllerConfig(),
},
expectedErr: nil,
},
}

for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
actualErr := tc.cfg.Validate()
if tc.expectedErr != nil {
require.EqualError(t, actualErr, tc.expectedErr.Error())
} else {
require.NoError(t, actualErr)
}

})
}
}
6 changes: 6 additions & 0 deletions receiver/tlscheckreceiver/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

//go:generate mdatagen metadata.yaml

package tlscheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver"
34 changes: 34 additions & 0 deletions receiver/tlscheckreceiver/documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[comment]: <> (Code generated by mdatagen. DO NOT EDIT.)

# tlscheck

## Default Metrics

The following metrics are emitted by default. Each of them can be disabled by applying the following configuration:

```yaml
metrics:
<metric_name>:
enabled: false
```
### tlscheck.time_left
Time in seconds until certificate expiry, as specified by `NotAfter` field in the x.509 certificate. Negative values represent time in seconds since expiration.

| Unit | Metric Type | Value Type |
| ---- | ----------- | ---------- |
| s | Gauge | Int |

#### Attributes

| Name | Description | Values |
| ---- | ----------- | ------ |
| tlscheck.x509.issuer | The entity that issued the certificate. | Any Str |
| tlscheck.x509.cn | The commonName in the subject of the certificate. | Any Str |

## Resource Attributes

| Name | Description | Values | Enabled |
| ---- | ----------- | ------ | ------- |
| tlscheck.url | Url at which the certificate was accessed. | Any Str | true |
62 changes: 62 additions & 0 deletions receiver/tlscheckreceiver/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package tlscheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver"

import (
"context"
"errors"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/consumer"
"go.opentelemetry.io/collector/receiver"
"go.opentelemetry.io/collector/receiver/scraperhelper"

"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/tlscheckreceiver/internal/metadata"
)

var (
errConfigNotTLSCheck = errors.New(`invalid config`)
)

// NewFactory creates a new filestats receiver factory.
func NewFactory() receiver.Factory {
return receiver.NewFactory(
metadata.Type,
newDefaultConfig,
receiver.WithMetrics(newReceiver, metadata.MetricsStability))
}

func newDefaultConfig() component.Config {
return &Config{
ControllerConfig: scraperhelper.NewDefaultControllerConfig(),
MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(),
Targets: []*targetConfig{},
}
}

func newReceiver(
_ context.Context,
settings receiver.Settings,
cfg component.Config,
consumer consumer.Metrics,
) (receiver.Metrics, error) {
tlsCheckConfig, ok := cfg.(*Config)
if !ok {
return nil, errConfigNotTLSCheck
}

mp := newScraper(tlsCheckConfig, settings)
s, err := scraperhelper.NewScraper(metadata.Type, mp.scrape)
if err != nil {
return nil, err
}
opt := scraperhelper.AddScraper(s)

return scraperhelper.NewScraperControllerReceiver(
&tlsCheckConfig.ControllerConfig,
settings,
consumer,
opt,
)
}
Loading

0 comments on commit a1d7a33

Please sign in to comment.