Skip to content

Commit

Permalink
[receiver/cloudflare] Make TLS config optional for cloudflarereceiver (
Browse files Browse the repository at this point in the history
…open-telemetry#26629)

Allow TLS for cloudflare receiver to be optional
  • Loading branch information
xbglowx authored and jorgeancal committed Sep 18, 2023
1 parent c50f352 commit 0045729
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 54 deletions.
27 changes: 27 additions & 0 deletions .chloggen/tls-optional-cloudflare-receiver.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: enhancement

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

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Make TLS config optional for cloudflarereceiver

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

# (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]
20 changes: 11 additions & 9 deletions receiver/cloudflarereceiver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,27 @@ This Cloudflare receiver allows Cloudflare's [LogPush Jobs](https://developers.c
To successfully operate this receiver, you must follow these steps in order:
1. Have a Cloudflare site at the Enterprise plan level.
- At the time the receiver was written, LogPush was available only for Enterprise sites.
2. Receive a properly CA signed SSL certificate for use on the collector host.
3. Configure the receiver using the previously acquired SSL certificate, and then start the collector.
4. Create a LogPush HTTP destination job following the [directions](https://developers.cloudflare.com/logs/get-started/enable-destinations/http/) provided by Cloudflare. When the job is created, it will attempt to validate the connection to the receiver.
1. Create a LogPush HTTP destination job following the [directions](https://developers.cloudflare.com/logs/get-started/enable-destinations/http/) provided by Cloudflare. When the job is created, it will attempt to validate the connection to the receiver.
- If you've configured the receiver with a `secret` to validate requests, ensure you add the value to the `destination_conf` parameter of the LogPush job by adding its value as a query parameter under the `header_X-CF-Secret` parameter. For example, `"destination_conf": "https://example.com?header_X-CF-Secret=abcd1234"`.
- If you want the receiver to parse one of the fields as the log record's timestamp (`EdgeStartTimestamp` is the default), the timestamp should be formatted RFC3339. This is not the default format, and must be explicitly specified in your job config.
- If using the deprecated `logpull_options` parameter to configure your job, this can be explicitly specified by adding `&timestamps=rfc3339` to the `logpull_options` string when creating your LogPush job.
- If using the `output_options` parameter to configure your job, this can be explicitly specified by setting the `timestamp_format` field of `output_options` to `"rfc3339"`
- The receiver expects the uploaded logs to be in `ndjson` format with no template, prefix, suffix, or delimiter changes based on the options in `output_options`. The only [settings](https://developers.cloudflare.com/logs/reference/log-output-options/#output-types) supported by this receiver in `output_options` are `field_names`, `CVE-2021-44228`, and `sample_rate`.
5. If the LogPush job creates successfully, the receiver is correctly configured and the LogPush job was able to send it a "test" message. If the job failed to create, the most likely issue is with the SSL configuration. Check both the LogPush API response and the receiver's logs for more details.
1. If the LogPush job creates successfully, the receiver is correctly configured and the LogPush job was able to send it a "test" message. If the job failed to create, the most likely issue is with the SSL configuration. Check both the LogPush API response and the receiver's logs for more details.

### Optional
If the receiver will be handling TLS termination:

1. Receive a properly CA signed SSL certificate for use on the collector host.
1. Configure the receiver using the previously acquired SSL certificate, and then start the collector.

## Configuration

- `tls` (Cloudflare requires TLS, and self-signed will not be sufficient)
- `cert_file`
- `tls` (Optional - Cloudflare requires TLS, and self-signed will not be sufficient)
- `cert_file`
- You may need to append your CA certificate to the server's certificate, if it is not a CA known to the LogPush API.
- `key_file`
- `endpoint`
- `endpoint`
- The endpoint on which the receiver will await requests from Cloudflare
- `secret`
- If this value is set, the receiver expects to see it in any valid requests under the `X-CF-Secret` header
Expand All @@ -64,5 +68,3 @@ receivers:
ClientIP: http_request.client_ip
ClientRequestURI: http_request.uri
```


23 changes: 11 additions & 12 deletions receiver/cloudflarereceiver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ type LogsConfig struct {

var (
errNoEndpoint = errors.New("an endpoint must be specified")
errNoTLS = errors.New("tls must be configured")
errNoCert = errors.New("tls was configured, but no cert file was specified")
errNoKey = errors.New("tls was configured, but no key file was specified")

Expand All @@ -39,23 +38,23 @@ func (c *Config) Validate() error {
return errNoEndpoint
}

if c.Logs.TLS == nil {
return errNoTLS
var errs error
if c.Logs.TLS != nil {
// Missing key
if c.Logs.TLS.KeyFile == "" {
errs = multierr.Append(errs, errNoKey)
}

// Missing cert
if c.Logs.TLS.CertFile == "" {
errs = multierr.Append(errs, errNoCert)
}
}

var errs error
_, _, err := net.SplitHostPort(c.Logs.Endpoint)
if err != nil {
errs = multierr.Append(errs, fmt.Errorf("failed to split endpoint into 'host:port' pair: %w", err))
}

if c.Logs.TLS.CertFile == "" {
errs = multierr.Append(errs, errNoCert)
}

if c.Logs.TLS.KeyFile == "" {
errs = multierr.Append(errs, errNoKey)
}

return errs
}
23 changes: 9 additions & 14 deletions receiver/cloudflarereceiver/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,18 @@ func TestValidate(t *testing.T) {
expectedErr string
}{
{
name: "Valid config with tls",
name: "Valid config",
config: Config{
Logs: LogsConfig{
Endpoint: "0.0.0.0:9999",
TLS: &configtls.TLSServerSetting{
TLSSetting: configtls.TLSSetting{
CertFile: "some_cert_file",
KeyFile: "some_key_file",
},
},
},
},
},
{
name: "missing endpoint",
name: "Valid config with tls",
config: Config{
Logs: LogsConfig{
Endpoint: "0.0.0.0:9999",
TLS: &configtls.TLSServerSetting{
TLSSetting: configtls.TLSSetting{
CertFile: "some_cert_file",
Expand All @@ -47,19 +42,19 @@ func TestValidate(t *testing.T) {
},
},
},
},
{
name: "missing endpoint",
config: Config{
Logs: LogsConfig{},
},
expectedErr: errNoEndpoint.Error(),
},
{
name: "Invalid endpoint",
config: Config{
Logs: LogsConfig{
Endpoint: "9999",
TLS: &configtls.TLSServerSetting{
TLSSetting: configtls.TLSSetting{
CertFile: "some_cert_file",
KeyFile: "some_key_file",
},
},
},
},
expectedErr: "failed to split endpoint into 'host:port' pair",
Expand Down
2 changes: 0 additions & 2 deletions receiver/cloudflarereceiver/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"context"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configtls"
"go.opentelemetry.io/collector/consumer"
"go.opentelemetry.io/collector/receiver"

Expand Down Expand Up @@ -37,7 +36,6 @@ func createDefaultConfig() component.Config {
return &Config{
Logs: LogsConfig{
TimestampField: defaultTimestampField,
TLS: &configtls.TLSServerSetting{},
},
}
}
52 changes: 35 additions & 17 deletions receiver/cloudflarereceiver/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,20 @@ func newLogsReceiver(params rcvr.CreateSettings, cfg *Config, consumer consumer.
id: params.ID,
}

tlsConfig, err := recv.cfg.TLS.LoadTLSConfig()
if err != nil {
return nil, err
}

s := &http.Server{
TLSConfig: tlsConfig,
recv.server = &http.Server{
Handler: http.HandlerFunc(recv.handleRequest),
ReadHeaderTimeout: 20 * time.Second,
}

recv.server = s
if recv.cfg.TLS != nil {
tlsConfig, err := recv.cfg.TLS.LoadTLSConfig()
if err != nil {
return nil, err
}

recv.server.TLSConfig = tlsConfig
}

return recv, nil
}

Expand Down Expand Up @@ -93,18 +95,34 @@ func (l *logsReceiver) startListening(ctx context.Context, host component.Host)
go func() {
defer l.wg.Done()

l.logger.Debug("Starting ServeTLS",
zap.String("address", l.cfg.Endpoint),
zap.String("certfile", l.cfg.TLS.CertFile),
zap.String("keyfile", l.cfg.TLS.KeyFile))
if l.cfg.TLS != nil {
l.logger.Debug("Starting ServeTLS",
zap.String("address", l.cfg.Endpoint),
zap.String("certfile", l.cfg.TLS.CertFile),
zap.String("keyfile", l.cfg.TLS.KeyFile))

err := l.server.ServeTLS(listener, l.cfg.TLS.CertFile, l.cfg.TLS.KeyFile)
err := l.server.ServeTLS(listener, l.cfg.TLS.CertFile, l.cfg.TLS.KeyFile)

l.logger.Debug("Serve TLS done")
l.logger.Debug("ServeTLS done")

if err != http.ErrServerClosed {
l.logger.Error("ServeTLS failed", zap.Error(err))
host.ReportFatalError(err)
}

} else {
l.logger.Debug("Starting Serve",
zap.String("address", l.cfg.Endpoint))

err := l.server.Serve(listener)

l.logger.Debug("Serve done")

if err != http.ErrServerClosed {
l.logger.Error("Serve failed", zap.Error(err))
host.ReportFatalError(err)
}

if err != http.ErrServerClosed {
l.logger.Error("ServeTLS failed", zap.Error(err))
host.ReportFatalError(err)
}
}()
return nil
Expand Down

0 comments on commit 0045729

Please sign in to comment.