Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TLS certificates #143

Merged
merged 16 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions pkg/remotewrite/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ type Config struct {
// Password is the Password for the Basic Auth.
Password null.String `json:"password"`

// ClientCertificate is the public key of the SSL certificate.
olegbespalov marked this conversation as resolved.
Show resolved Hide resolved
ClientCertificate null.String `json:"clientCertificate"`

// ClientCertificateKey is the private key of the SSL certificate.
ClientCertificateKey null.String `json:"clientCertificateKey"`

// PushInterval defines the time between flushes. The Output will wait the set time
// before push a new set of time series to the endpoint.
PushInterval types.NullDuration `json:"pushInterval"`
Expand Down Expand Up @@ -89,6 +95,11 @@ func (conf Config) RemoteConfig() (*remote.HTTPConfig, error) {
InsecureSkipVerify: conf.InsecureSkipTLSVerify.Bool, //nolint:gosec
}

if conf.ClientCertificate.Valid && conf.ClientCertificateKey.Valid {
cert, _ := tls.LoadX509KeyPair(conf.ClientCertificate.String, conf.ClientCertificateKey.String)
olegbespalov marked this conversation as resolved.
Show resolved Hide resolved
hc.TLSConfig.Certificates = []tls.Certificate{cert}
}

if len(conf.Headers) > 0 {
hc.Headers = make(http.Header)
for k, v := range conf.Headers {
Expand Down Expand Up @@ -139,6 +150,14 @@ func (conf Config) Apply(applied Config) Config {
copy(conf.TrendStats, applied.TrendStats)
}

if applied.ClientCertificate.Valid {
conf.ClientCertificate = applied.ClientCertificate
}

if applied.ClientCertificateKey.Valid {
conf.ClientCertificateKey = applied.ClientCertificateKey
}

return conf
}

Expand Down Expand Up @@ -229,6 +248,14 @@ func parseEnvs(env map[string]string) (Config, error) {
c.Password = null.StringFrom(password)
}

if clientCertificate, certDefined := env["K6_PROMETHEUS_RW_CLIENT_CERTIFICATE"]; certDefined {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if clientCertificate, certDefined := env["K6_PROMETHEUS_RW_CLIENT_CERTIFICATE"]; certDefined {
if clientCertificate, certDefined := env["K6_PROMETHEUS_RW_TLS_CLIENT_CERTIFICATE"]; certDefined {

Do you think we should be more specific about the naming?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both fine fro me 🤷

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there something else that we can have a client certificate for ?

If not I am also 🤷 - if yes , then I would prefer to have mtls even.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, or at least I didn't find it. At the moment, if we mention client certificates then they should set automatically the context for TLS, but I can't guarantee for the future.

Copy link
Contributor Author

@codebien codebien Sep 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are doing mostly the same with Username and Password where we aren't specifying that is for BasicAuth.

c.ClientCertificate = null.StringFrom(clientCertificate)
}

if clientCertificateKey, certDefined := env["K6_PROMETHEUS_RW_CLIENT_CERTIFICATE_KEY"]; certDefined {
c.ClientCertificateKey = null.StringFrom(clientCertificateKey)
}

envHeaders := getEnvMap(env, "K6_PROMETHEUS_RW_HEADERS_")
for k, v := range envHeaders {
if c.Headers == nil {
Expand Down Expand Up @@ -304,6 +331,11 @@ func parseArg(text string) (Config, error) {
//c.TrendStats = strings.Split(v, ",")
//}

case "clientCertificate":
c.ClientCertificate = null.StringFrom(v)
case "clientCertificateKey":
c.ClientCertificateKey = null.StringFrom(v)

default:
if !strings.HasPrefix(key, "headers.") {
return c, fmt.Errorf("%q is an unknown option's key", r[0])
Expand Down
37 changes: 35 additions & 2 deletions pkg/remotewrite/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,41 @@ func TestOptionBasicAuth(t *testing.T) {
}
}

func TestOptionClientCertificate(t *testing.T) {
t.Parallel()

cases := map[string]struct {
arg string
env map[string]string
jsonRaw json.RawMessage
}{
"JSON": {jsonRaw: json.RawMessage(`{"clientCertificate":"client.crt","clientCertificateKey":"client.key"}`)},
"Env": {env: map[string]string{"K6_PROMETHEUS_RW_CLIENT_CERTIFICATE": "client.crt", "K6_PROMETHEUS_RW_CLIENT_CERTIFICATE_KEY": "client.key"}},
}

expconfig := Config{
ServerURL: null.StringFrom("http://localhost:9090/api/v1/write"),
InsecureSkipTLSVerify: null.BoolFrom(false),
PushInterval: types.NullDurationFrom(5 * time.Second),
Headers: make(map[string]string),
TrendStats: []string{"p(99)"},
ClientCertificate: null.StringFrom("client.crt"),
ClientCertificateKey: null.StringFrom("client.key"),
StaleMarkers: null.BoolFrom(false),
}

for name, tc := range cases {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
c, err := GetConsolidatedConfig(
tc.jsonRaw, tc.env, tc.arg)
require.NoError(t, err)
assert.Equal(t, expconfig, c)
})
}
}

func TestOptionTrendAsNativeHistogram(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -403,8 +438,6 @@ func TestOptionPushInterval(t *testing.T) {
}{
"JSON": {jsonRaw: json.RawMessage(`{"pushInterval":"1m2s"}`)},
"Env": {env: map[string]string{"K6_PROMETHEUS_RW_PUSH_INTERVAL": "1m2s"}},
//nolint:gocritic
//"Arg": {arg: "pushInterval=1m2s"},
}

expconfig := Config{
Expand Down