Skip to content

Commit 7959020

Browse files
s-v-a-naishyandapalli
authored andcommitted
[receiver/mysql] expose tls config (open-telemetry#29269)
**Description:** Using the mysqlreceiver, we were getting the following error as our MySQL server on AWS RDS requires secure transport for all connections by setting `require_secure_transport=ON` per https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/mysql-ssl-connections.html#mysql-ssl-connections.require-ssl **Example log message** `2023-10-31T10:53:30.239Z error scraperhelper/scrapercontroller.go:200 Error scraping metrics {"kind": "receiver", "name": "mysql", "data_type": "metrics", "error": "Error 3159 (HY000): Connections using insecure transport are prohibited while --require_secure_transport=ON.; ", "scraper": "mysql"}`
1 parent aed34ba commit 7959020

File tree

10 files changed

+162
-5
lines changed

10 files changed

+162
-5
lines changed

.chloggen/main.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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: mysqlreceiver
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: "expose tls in mysqlreceiver"
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: [29269]
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: "If `tls` is not set, the default is to disable TLS connections."
19+
20+
21+
# If your change doesn't affect end users or the exported elements of any package,
22+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
23+
# Optional: The change log or logs in which this entry should be included.
24+
# e.g. '[user]' or '[user, api]'
25+
# Include 'user' if the change is relevant to end users.
26+
# Include 'api' if there is a change to a library API.
27+
# Default: '[user]'
28+
change_logs: []

receiver/mysqlreceiver/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ Collecting most metrics requires the ability to execute `SHOW GLOBAL STATUS`.
3030

3131
The following settings are optional:
3232
- `endpoint`: (default = `localhost:3306`)
33+
- `tls`: Defines the TLS configuration to use. If `tls` is not set, the default is to disable TLS connections.
34+
- `insecure`: (default = `false`) Set this to `true` to disable TLS connections.
35+
- `insecure_skip_verify`: (default = `false`) Set this to `true` to enable TLS but not verify the certificate.
36+
- `server_name_override`: This sets the ServerName in the TLSConfig.
3337
- `username`: (default = `root`)
3438
- `password`: The password to the username.
3539
- `allow_native_passwords`: (default = `true`)

receiver/mysqlreceiver/client.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,14 +163,29 @@ type ReplicaStatusStats struct {
163163

164164
var _ client = (*mySQLClient)(nil)
165165

166-
func newMySQLClient(conf *Config) client {
166+
func newMySQLClient(conf *Config) (client, error) {
167+
tls, err := conf.TLS.LoadTLSConfig()
168+
if err != nil {
169+
return nil, err
170+
}
171+
tlsConfig := ""
172+
if tls != nil {
173+
err := mysql.RegisterTLSConfig("custom", tls)
174+
if err != nil {
175+
return nil, err
176+
}
177+
tlsConfig = "custom"
178+
}
179+
167180
driverConf := mysql.Config{
168181
User: conf.Username,
169182
Passwd: string(conf.Password),
170183
Net: conf.Transport,
171184
Addr: conf.Endpoint,
172185
DBName: conf.Database,
173186
AllowNativePasswords: conf.AllowNativePasswords,
187+
TLS: tls,
188+
TLSConfig: tlsConfig,
174189
}
175190
connStr := driverConf.FormatDSN()
176191

@@ -179,7 +194,7 @@ func newMySQLClient(conf *Config) client {
179194
statementEventsDigestTextLimit: conf.StatementEvents.DigestTextLimit,
180195
statementEventsLimit: conf.StatementEvents.Limit,
181196
statementEventsTimeLimit: conf.StatementEvents.TimeLimit,
182-
}
197+
}, nil
183198
}
184199

185200
func (c *mySQLClient) Connect() error {

receiver/mysqlreceiver/config.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88

99
"go.opentelemetry.io/collector/config/confignet"
1010
"go.opentelemetry.io/collector/config/configopaque"
11+
"go.opentelemetry.io/collector/config/configtls"
12+
"go.opentelemetry.io/collector/confmap"
1113
"go.opentelemetry.io/collector/receiver/scraperhelper"
1214

1315
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/mysqlreceiver/internal/metadata"
@@ -26,6 +28,7 @@ type Config struct {
2628
Database string `mapstructure:"database,omitempty"`
2729
AllowNativePasswords bool `mapstructure:"allow_native_passwords,omitempty"`
2830
confignet.NetAddr `mapstructure:",squash"`
31+
TLS configtls.TLSClientSetting `mapstructure:"tls,omitempty"`
2932
MetricsBuilderConfig metadata.MetricsBuilderConfig `mapstructure:",squash"`
3033
StatementEvents StatementEventsConfig `mapstructure:"statement_events"`
3134
}
@@ -35,3 +38,19 @@ type StatementEventsConfig struct {
3538
Limit int `mapstructure:"limit"`
3639
TimeLimit time.Duration `mapstructure:"time_limit"`
3740
}
41+
42+
func (cfg *Config) Unmarshal(componentParser *confmap.Conf) error {
43+
if componentParser == nil {
44+
// Nothing to do if there is no config given.
45+
return nil
46+
}
47+
48+
// Change the default to Insecure = true as we don't want to break
49+
// existing deployments which does not use TLS by default.
50+
if !componentParser.IsSet("tls") {
51+
cfg.TLS = configtls.TLSClientSetting{}
52+
cfg.TLS.Insecure = true
53+
}
54+
55+
return componentParser.Unmarshal(cfg, confmap.WithErrorUnused())
56+
}

receiver/mysqlreceiver/config_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,32 @@ func TestLoadConfig(t *testing.T) {
3232
expected.Password = "${env:MYSQL_PASSWORD}"
3333
expected.Database = "otel"
3434
expected.CollectionInterval = 10 * time.Second
35+
// This defaults to true when tls is omitted from the configmap.
36+
expected.TLS.Insecure = true
37+
38+
require.Equal(t, expected, cfg)
39+
}
40+
41+
func TestLoadConfigDefaultTLS(t *testing.T) {
42+
cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
43+
require.NoError(t, err)
44+
45+
factory := NewFactory()
46+
cfg := factory.CreateDefaultConfig()
47+
48+
sub, err := cm.Sub(component.NewIDWithName(metadata.Type, "").String() + "/default_tls")
49+
require.NoError(t, err)
50+
require.NoError(t, component.UnmarshalConfig(sub, cfg))
51+
52+
expected := factory.CreateDefaultConfig().(*Config)
53+
expected.Endpoint = "localhost:3306"
54+
expected.Username = "otel"
55+
expected.Password = "${env:MYSQL_PASSWORD}"
56+
expected.Database = "otel"
57+
expected.CollectionInterval = 10 * time.Second
58+
// This defaults to false when tls is defined in the configmap.
59+
expected.TLS.Insecure = false
60+
expected.TLS.ServerName = "localhost"
3561

3662
require.Equal(t, expected, cfg)
3763
}

receiver/mysqlreceiver/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ require (
1313
go.opentelemetry.io/collector/component v0.89.0
1414
go.opentelemetry.io/collector/config/confignet v0.89.0
1515
go.opentelemetry.io/collector/config/configopaque v0.89.0
16+
go.opentelemetry.io/collector/config/configtls v0.89.0
1617
go.opentelemetry.io/collector/confmap v0.89.0
1718
go.opentelemetry.io/collector/consumer v0.89.0
1819
go.opentelemetry.io/collector/pdata v1.0.0-rcv0018
@@ -35,6 +36,7 @@ require (
3536
github.com/docker/docker v24.0.7+incompatible // indirect
3637
github.com/docker/go-connections v0.4.0 // indirect
3738
github.com/docker/go-units v0.5.0 // indirect
39+
github.com/fsnotify/fsnotify v1.7.0 // indirect
3840
github.com/go-ole/go-ole v1.2.6 // indirect
3941
github.com/gogo/protobuf v1.3.2 // indirect
4042
github.com/golang/protobuf v1.5.3 // indirect

receiver/mysqlreceiver/go.sum

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

receiver/mysqlreceiver/integration_test.go

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222

2323
const mysqlPort = "3306"
2424

25-
func TestIntegration(t *testing.T) {
25+
func TestIntegrationWithoutTLS(t *testing.T) {
2626
scraperinttest.NewIntegrationTest(
2727
NewFactory(),
2828
scraperinttest.WithContainerRequest(
@@ -52,6 +52,54 @@ func TestIntegration(t *testing.T) {
5252
rCfg.Endpoint = net.JoinHostPort(ci.Host(t), ci.MappedPort(t, mysqlPort))
5353
rCfg.Username = "otel"
5454
rCfg.Password = "otel"
55+
// disable TLS connection
56+
rCfg.TLS.Insecure = true
57+
}),
58+
scraperinttest.WithCompareOptions(
59+
pmetrictest.IgnoreResourceAttributeValue("mysql.instance.endpoint"),
60+
pmetrictest.IgnoreMetricValues(),
61+
pmetrictest.IgnoreMetricDataPointsOrder(),
62+
pmetrictest.IgnoreStartTimestamp(),
63+
pmetrictest.IgnoreTimestamp(),
64+
),
65+
).Run(t)
66+
}
67+
68+
func TestIntegrationWithTLS(t *testing.T) {
69+
scraperinttest.NewIntegrationTest(
70+
NewFactory(),
71+
scraperinttest.WithContainerRequest(
72+
testcontainers.ContainerRequest{
73+
Image: "mysql:8.0.33",
74+
// enable auto TLS certs AND require TLS connections only !
75+
Cmd: []string{"--auto_generate_certs=ON", "--require_secure_transport=ON"},
76+
ExposedPorts: []string{mysqlPort},
77+
WaitingFor: wait.ForListeningPort(mysqlPort).
78+
WithStartupTimeout(2 * time.Minute),
79+
Env: map[string]string{
80+
"MYSQL_ROOT_PASSWORD": "otel",
81+
"MYSQL_DATABASE": "otel",
82+
"MYSQL_USER": "otel",
83+
"MYSQL_PASSWORD": "otel",
84+
},
85+
Files: []testcontainers.ContainerFile{
86+
{
87+
HostFilePath: filepath.Join("testdata", "integration", "init.sh"),
88+
ContainerFilePath: "/docker-entrypoint-initdb.d/init.sh",
89+
FileMode: 700,
90+
},
91+
},
92+
}),
93+
scraperinttest.WithCustomConfig(
94+
func(t *testing.T, cfg component.Config, ci *scraperinttest.ContainerInfo) {
95+
rCfg := cfg.(*Config)
96+
rCfg.CollectionInterval = time.Second
97+
rCfg.Endpoint = net.JoinHostPort(ci.Host(t), ci.MappedPort(t, mysqlPort))
98+
rCfg.Username = "otel"
99+
rCfg.Password = "otel"
100+
// mysql container is using self-signed certs
101+
// InsecureSkipVerify will enable TLS but not verify the certificate.
102+
rCfg.TLS.InsecureSkipVerify = true
55103
}),
56104
scraperinttest.WithCompareOptions(
57105
pmetrictest.IgnoreResourceAttributeValue("mysql.instance.endpoint"),

receiver/mysqlreceiver/scraper.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,12 @@ func newMySQLScraper(
4646

4747
// start starts the scraper by initializing the db client connection.
4848
func (m *mySQLScraper) start(_ context.Context, _ component.Host) error {
49-
sqlclient := newMySQLClient(m.config)
49+
sqlclient, err := newMySQLClient(m.config)
50+
if err != nil {
51+
return err
52+
}
5053

51-
err := sqlclient.Connect()
54+
err = sqlclient.Connect()
5255
if err != nil {
5356
return err
5457
}

receiver/mysqlreceiver/testdata/config.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,11 @@ mysql:
44
password: ${env:MYSQL_PASSWORD}
55
database: otel
66
collection_interval: 10s
7+
mysql/default_tls:
8+
endpoint: localhost:3306
9+
username: otel
10+
password: ${env:MYSQL_PASSWORD}
11+
database: otel
12+
collection_interval: 10s
13+
tls: # specified, but use default values
14+
server_name_override: localhost

0 commit comments

Comments
 (0)