Skip to content

Commit 83aa78f

Browse files
authored
Add mtls client auth (#5)
1 parent 3dac9c7 commit 83aa78f

File tree

4 files changed

+83
-8
lines changed

4 files changed

+83
-8
lines changed

client.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@ type Client struct {
1313
es *elasticsearch.Client
1414
}
1515

16-
func NewClient(addresses []string, insecure bool) (*Client, error) {
16+
func NewClient(addresses []string, tlsClientConfig *tls.Config) (*Client, error) {
1717
cfg := elasticsearch.Config{
1818
Addresses: addresses,
1919
Transport: &http.Transport{
20-
TLSClientConfig: &tls.Config{
21-
InsecureSkipVerify: insecure,
22-
},
20+
TLSClientConfig: tlsClientConfig,
2321
},
2422
}
2523

collector.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"crypto/tls"
45
"fmt"
56
"regexp"
67
"strings"
@@ -21,12 +22,12 @@ type Collector struct {
2122
docsCount *prometheus.Desc
2223
}
2324

24-
func NewCollector(address, project string, insecure bool) (*Collector, error) {
25+
func NewCollector(address, project string, tlsClientConfig *tls.Config) (*Collector, error) {
2526
namespace := "oneday_elasticsearch"
2627
labels := []string{"index", "index_group"}
2728
labels_group := []string{"index_group"}
2829

29-
client, err := NewClient([]string{address}, insecure)
30+
client, err := NewClient([]string{address}, tlsClientConfig)
3031
if err != nil {
3132
return nil, fmt.Errorf("error creating the client: %v", err)
3233
}

main.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,13 @@ var (
3939

4040
address = kingpin.Flag("address", "Elasticsearch node to use.").
4141
Default("http://localhost:9200").String()
42-
insecure = kingpin.Flag("insecure", "Allow insecure server connections when using SSL.").
42+
cacert = kingpin.Flag("ca-cert", "Path to PEM file that contains trusted Certificate Authorities for the Elasticsearch connection.").
43+
Default("").String()
44+
clientcert = kingpin.Flag("client-cert", "Path to PEM file that contains the corresponding cert for the private key to connect to Elasticsearch.").
45+
Default("").String()
46+
clientkey = kingpin.Flag("client-key", "Path to PEM file that contains the private key for client auth when connecting to Elasticsearch.").
47+
Default("").String()
48+
insecure = kingpin.Flag("insecure", "Skip SSL verification when connecting to Elasticsearch.").
4349
Default("false").Bool()
4450

4551
projectName = kingpin.Flag("project", "Project name").String()
@@ -88,7 +94,9 @@ func main() {
8894
log.Info("Starting es-oneday-exporter", version.Info())
8995
log.Info("Build context", version.BuildContext())
9096

91-
collector, err := NewCollector(*address, *projectName, *insecure)
97+
tlsClientConfig := createTLSConfig(*cacert, *clientcert, *clientkey, *insecure)
98+
99+
collector, err := NewCollector(*address, *projectName, tlsClientConfig)
92100
if err != nil {
93101
log.Fatal("error creating new collector instance: ", err)
94102
}

tls.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2021 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package main
15+
16+
import (
17+
"crypto/tls"
18+
"crypto/x509"
19+
"io/ioutil"
20+
)
21+
22+
func createTLSConfig(pemFile, pemCertFile, pemPrivateKeyFile string, insecureSkipVerify bool) *tls.Config {
23+
tlsConfig := tls.Config{}
24+
if insecureSkipVerify {
25+
// pem settings are irrelevant if we're skipping verification anyway
26+
tlsConfig.InsecureSkipVerify = true
27+
}
28+
if len(pemFile) > 0 {
29+
rootCerts, err := loadCertificatesFrom(pemFile)
30+
if err != nil {
31+
log.Fatalf("Couldn't load root certificate from %s. Got %s.", pemFile, err)
32+
return nil
33+
}
34+
tlsConfig.RootCAs = rootCerts
35+
}
36+
if len(pemCertFile) > 0 && len(pemPrivateKeyFile) > 0 {
37+
// Load files once to catch configuration error early.
38+
_, err := loadPrivateKeyFrom(pemCertFile, pemPrivateKeyFile)
39+
if err != nil {
40+
log.Fatalf("Couldn't setup client authentication. Got %s.", err)
41+
return nil
42+
}
43+
// Define a function to load certificate and key lazily at TLS handshake to
44+
// ensure that the latest files are used in case they have been rotated.
45+
tlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
46+
return loadPrivateKeyFrom(pemCertFile, pemPrivateKeyFile)
47+
}
48+
}
49+
return &tlsConfig
50+
}
51+
52+
func loadCertificatesFrom(pemFile string) (*x509.CertPool, error) {
53+
caCert, err := ioutil.ReadFile(pemFile)
54+
if err != nil {
55+
return nil, err
56+
}
57+
certificates := x509.NewCertPool()
58+
certificates.AppendCertsFromPEM(caCert)
59+
return certificates, nil
60+
}
61+
62+
func loadPrivateKeyFrom(pemCertFile, pemPrivateKeyFile string) (*tls.Certificate, error) {
63+
privateKey, err := tls.LoadX509KeyPair(pemCertFile, pemPrivateKeyFile)
64+
if err != nil {
65+
return nil, err
66+
}
67+
return &privateKey, nil
68+
}

0 commit comments

Comments
 (0)