Skip to content

Commit d9769f7

Browse files
[exporter/clickhouse] TLS config / Certificate based Authentication (#42581)
#### Description Enables usage of OpenTelemetry's TLS configuration structure for clickhouse-go driver. Example: ```yaml exporters: clickhouse: endpoint: tcp://127.0.0.1:9000 tls: insecure: false insecure_skip_verify: false ca_file: CAroot.crt cert_file: client.crt key_file: client.key ``` #### Link to tracking issue Fixes #22805, #41116 #### Testing Added integration test that uses locally generated test certificates to test certificate authentication with ClickHouse. Certificates can be regenerated with `./testdata/certs/gen-certs.sh`. Also updated ClickHouse image versions to latest stable/LTS. #### Documentation Updated README with `tls` config description and example.
1 parent d4f9a62 commit d9769f7

23 files changed

+462
-40
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: clickhouseexporter
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add TLS configuration for cert based authentication.
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [22805]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# If your change doesn't affect end users or the exported elements of any package,
21+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
22+
# Optional: The change log or logs in which this entry should be included.
23+
# e.g. '[user]' or '[user, api]'
24+
# Include 'user' if the change is relevant to end users.
25+
# Include 'api' if there is a change to a library API.
26+
# Default: '[user]'
27+
change_logs: []

exporter/clickhouseexporter/README.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ Connection options:
290290
- `create_schema` (default = true): When set to true, will run DDL to create the database and tables. (See [schema management](#schema-management))
291291
- `compress` (default = lz4): Controls the compression algorithm. Valid options: `none` (disabled), `zstd`, `lz4` (default), `gzip`, `deflate`, `br`, `true` (lz4). Ignored if `compress` is set in the `endpoint` or `connection_params`.
292292
- `async_insert` (default = true): Enables [async inserts](https://clickhouse.com/docs/en/optimize/asynchronous-inserts). Ignored if async inserts are configured in the `endpoint` or `connection_params`. Async inserts may still be overridden server-side.
293+
- `tls` Advanced TLS configuration (See [TLS](#tls)).
293294

294295
Additional DSN features:
295296

@@ -342,8 +343,23 @@ Processing:
342343

343344
## TLS
344345

345-
The exporter supports TLS. To enable TLS, you need to specify the `secure=true` query parameter in the `endpoint` URL or
346-
use the `https` scheme.
346+
The exporter supports TLS. To enable TLS, you must specify the `secure=true` query parameter in the `endpoint` URL or use the `https` scheme.
347+
348+
You may also use certificate authentication with the `tls` setting:
349+
350+
```yaml
351+
exporters:
352+
clickhouse:
353+
endpoint: . . .
354+
tls:
355+
insecure: false
356+
insecure_skip_verify: false
357+
ca_file: CAroot.crt
358+
cert_file: client.crt
359+
key_file: client.key
360+
```
361+
362+
The available `tls` options are inherited from [OpenTelemetry's TLS config structure](https://pkg.go.dev/go.opentelemetry.io/collector/config/configtls#ClientConfig), more options are available than shown in this example.
347363

348364
## Schema management
349365

exporter/clickhouseexporter/config.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package clickhouseexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/clickhouseexporter"
55

66
import (
7+
"context"
78
"errors"
89
"fmt"
910
"net/url"
@@ -13,6 +14,7 @@ import (
1314
"go.opentelemetry.io/collector/component"
1415
"go.opentelemetry.io/collector/config/configopaque"
1516
"go.opentelemetry.io/collector/config/configretry"
17+
"go.opentelemetry.io/collector/config/configtls"
1618
"go.opentelemetry.io/collector/exporter/exporterhelper"
1719

1820
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/clickhouseexporter/internal"
@@ -36,6 +38,8 @@ type Config struct {
3638
Password configopaque.String `mapstructure:"password"`
3739
// Database is the database name to export.
3840
Database string `mapstructure:"database"`
41+
// TLS is the TLS config for connecting to ClickHouse.
42+
TLS configtls.ClientConfig `mapstructure:"tls"`
3943
// ConnectionParams is the extra connection parameters with map format. for example compression/dial_timeout
4044
ConnectionParams map[string]string `mapstructure:"connection_params"`
4145
// LogsTableName is the table name for logs. default is `otel_logs`.
@@ -195,6 +199,27 @@ func (cfg *Config) buildDSN() (string, error) {
195199
return dsnURL.String(), nil
196200
}
197201

202+
func (cfg *Config) buildClickHouseOptions() (*clickhouse.Options, error) {
203+
dsn, err := cfg.buildDSN()
204+
if err != nil {
205+
return nil, fmt.Errorf("failed to build DSN from config: %w", err)
206+
}
207+
208+
opt, err := clickhouse.ParseDSN(dsn)
209+
if err != nil {
210+
return nil, fmt.Errorf("failed to parse DSN: %w", err)
211+
}
212+
213+
if cfg.TLS.CertFile != "" || cfg.TLS.KeyFile != "" {
214+
opt.TLS, err = cfg.TLS.LoadTLSConfig(context.Background())
215+
if err != nil {
216+
return nil, fmt.Errorf("failed to load TLS config: %w", err)
217+
}
218+
}
219+
220+
return opt, nil
221+
}
222+
198223
// shouldCreateSchema returns true if the exporter should run the DDL for creating database/tables.
199224
func (cfg *Config) shouldCreateSchema() bool {
200225
return cfg.CreateSchema

exporter/clickhouseexporter/config_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"go.opentelemetry.io/collector/component"
1717
"go.opentelemetry.io/collector/config/configopaque"
1818
"go.opentelemetry.io/collector/config/configretry"
19+
"go.opentelemetry.io/collector/config/configtls"
1920
"go.opentelemetry.io/collector/confmap/confmaptest"
2021
"go.opentelemetry.io/collector/confmap/xconfmap"
2122
"go.opentelemetry.io/collector/exporter/exporterhelper"
@@ -84,6 +85,12 @@ func TestLoadConfig(t *testing.T) {
8485
Sizer: exporterhelper.RequestSizerTypeRequests,
8586
},
8687
AsyncInsert: true,
88+
TLS: configtls.ClientConfig{
89+
Config: configtls.Config{
90+
CertFile: "client.crt",
91+
KeyFile: "client.key",
92+
},
93+
},
8794
},
8895
},
8996
}

exporter/clickhouseexporter/exporter_logs.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ func newLogsExporter(logger *zap.Logger, cfg *Config) *logsExporter {
3535
}
3636

3737
func (e *logsExporter) start(ctx context.Context, _ component.Host) error {
38-
dsn, err := e.cfg.buildDSN()
38+
opt, err := e.cfg.buildClickHouseOptions()
3939
if err != nil {
4040
return err
4141
}
4242

43-
e.db, err = internal.NewClickhouseClient(dsn)
43+
e.db, err = internal.NewClickhouseClientFromOptions(opt)
4444
if err != nil {
4545
return err
4646
}

exporter/clickhouseexporter/exporter_logs_json.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ func newLogsJSONExporter(logger *zap.Logger, cfg *Config) *logsJSONExporter {
3434
}
3535

3636
func (e *logsJSONExporter) start(ctx context.Context, _ component.Host) error {
37-
dsn, err := e.cfg.buildDSN()
37+
opt, err := e.cfg.buildClickHouseOptions()
3838
if err != nil {
3939
return err
4040
}
4141

42-
e.db, err = internal.NewClickhouseClient(dsn)
42+
e.db, err = internal.NewClickhouseClientFromOptions(opt)
4343
if err != nil {
4444
return err
4545
}

exporter/clickhouseexporter/exporter_metrics.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ func newMetricsExporter(logger *zap.Logger, cfg *Config) *metricsExporter {
3737
func (e *metricsExporter) start(ctx context.Context, _ component.Host) error {
3838
metrics.SetLogger(e.logger)
3939

40-
dsn, err := e.cfg.buildDSN()
40+
opt, err := e.cfg.buildClickHouseOptions()
4141
if err != nil {
4242
return err
4343
}
4444

45-
e.db, err = internal.NewClickhouseClient(dsn)
45+
e.db, err = internal.NewClickhouseClientFromOptions(opt)
4646
if err != nil {
4747
return err
4848
}

exporter/clickhouseexporter/exporter_traces.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ func newTracesExporter(logger *zap.Logger, cfg *Config) *tracesExporter {
3636
}
3737

3838
func (e *tracesExporter) start(ctx context.Context, _ component.Host) error {
39-
dsn, err := e.cfg.buildDSN()
39+
opt, err := e.cfg.buildClickHouseOptions()
4040
if err != nil {
4141
return err
4242
}
4343

44-
e.db, err = internal.NewClickhouseClient(dsn)
44+
e.db, err = internal.NewClickhouseClientFromOptions(opt)
4545
if err != nil {
4646
return err
4747
}

exporter/clickhouseexporter/exporter_traces_json.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ func newTracesJSONExporter(logger *zap.Logger, cfg *Config) *tracesJSONExporter
3434
}
3535

3636
func (e *tracesJSONExporter) start(ctx context.Context, _ component.Host) error {
37-
dsn, err := e.cfg.buildDSN()
37+
opt, err := e.cfg.buildClickHouseOptions()
3838
if err != nil {
3939
return err
4040
}
4141

42-
e.db, err = internal.NewClickhouseClient(dsn)
42+
e.db, err = internal.NewClickhouseClientFromOptions(opt)
4343
if err != nil {
4444
return err
4545
}

exporter/clickhouseexporter/go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ require (
1212
go.opentelemetry.io/collector/component/componenttest v0.135.1-0.20250911155607-37a3ace6274c
1313
go.opentelemetry.io/collector/config/configopaque v1.41.1-0.20250911155607-37a3ace6274c
1414
go.opentelemetry.io/collector/config/configretry v1.41.1-0.20250911155607-37a3ace6274c
15+
go.opentelemetry.io/collector/config/configtls v1.41.1-0.20250911155607-37a3ace6274c
1516
go.opentelemetry.io/collector/confmap v1.41.1-0.20250911155607-37a3ace6274c
1617
go.opentelemetry.io/collector/confmap/xconfmap v0.135.1-0.20250911155607-37a3ace6274c
1718
go.opentelemetry.io/collector/exporter v0.135.1-0.20250911155607-37a3ace6274c
@@ -43,6 +44,8 @@ require (
4344
github.com/docker/go-units v0.5.0 // indirect
4445
github.com/ebitengine/purego v0.8.4 // indirect
4546
github.com/felixge/httpsnoop v1.0.4 // indirect
47+
github.com/foxboron/go-tpm-keyfiles v0.0.0-20250323135004-b31fac66206e // indirect
48+
github.com/fsnotify/fsnotify v1.9.0 // indirect
4649
github.com/go-faster/city v1.0.1 // indirect
4750
github.com/go-faster/errors v0.7.1 // indirect
4851
github.com/go-logr/logr v1.4.3 // indirect
@@ -51,6 +54,7 @@ require (
5154
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
5255
github.com/gobwas/glob v0.2.3 // indirect
5356
github.com/gogo/protobuf v1.3.2 // indirect
57+
github.com/google/go-tpm v0.9.5 // indirect
5458
github.com/google/uuid v1.6.0 // indirect
5559
github.com/hashicorp/go-version v1.7.0 // indirect
5660
github.com/json-iterator/go v1.1.12 // indirect

0 commit comments

Comments
 (0)