Skip to content

Commit c809deb

Browse files
allingeekdanielnelson
authored andcommitted
TLS and MTLS enhancements to HTTPListener input plugin (#3191)
1 parent 247c2e7 commit c809deb

File tree

3 files changed

+357
-41
lines changed

3 files changed

+357
-41
lines changed

plugins/inputs/http_listener/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ The `/write` endpoint supports the `precision` query parameter and can be set to
88

99
When chaining Telegraf instances using this plugin, CREATE DATABASE requests receive a 200 OK response with message body `{"results":[]}` but they are not relayed. The output configuration of the Telegraf instance which ultimately submits data to InfluxDB determines the destination database.
1010

11+
Enable TLS by specifying the file names of a service TLS certificate and key.
12+
13+
Enable mutually authenticated TLS and authorize client connections by signing certificate authority by including a list of allowed CA certificate file names in ````tls_allowed_cacerts````.
14+
1115
See: [Telegraf Input Data Formats](https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md#influx).
1216

1317
**Example:**
@@ -28,4 +32,11 @@ This is a sample configuration for the plugin.
2832
## timeouts
2933
read_timeout = "10s"
3034
write_timeout = "10s"
35+
36+
## HTTPS
37+
tls_cert= "/etc/telegraf/cert.pem"
38+
tls_key = "/etc/telegraf/key.pem"
39+
40+
## MTLS
41+
tls_allowed_cacerts = ["/etc/telegraf/clientca.pem"]
3142
```

plugins/inputs/http_listener/http_listener.go

Lines changed: 72 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ package http_listener
33
import (
44
"bytes"
55
"compress/gzip"
6+
"crypto/tls"
7+
"crypto/x509"
68
"io"
9+
"io/ioutil"
710
"log"
811
"net"
912
"net/http"
@@ -37,6 +40,10 @@ type HTTPListener struct {
3740
MaxLineSize int
3841
Port int
3942

43+
TlsAllowedCacerts []string
44+
TlsCert string
45+
TlsKey string
46+
4047
mu sync.Mutex
4148
wg sync.WaitGroup
4249

@@ -75,6 +82,14 @@ const sampleConfig = `
7582
## Maximum line size allowed to be sent in bytes.
7683
## 0 means to use the default of 65536 bytes (64 kibibytes)
7784
max_line_size = 0
85+
86+
## Set one or more allowed client CA certificate file names to
87+
## enable mutually authenticated TLS connections
88+
tls_allowed_cacerts = ["/etc/telegraf/clientca.pem"]
89+
90+
## Add service certificate and key
91+
tls_cert = "/etc/telegraf/cert.pem"
92+
tls_key = "/etc/telegraf/key.pem"
7893
`
7994

8095
func (h *HTTPListener) SampleConfig() string {
@@ -117,10 +132,33 @@ func (h *HTTPListener) Start(acc telegraf.Accumulator) error {
117132
h.MaxLineSize = DEFAULT_MAX_LINE_SIZE
118133
}
119134

135+
if h.ReadTimeout.Duration < time.Second {
136+
h.ReadTimeout.Duration = time.Second * 10
137+
}
138+
if h.WriteTimeout.Duration < time.Second {
139+
h.WriteTimeout.Duration = time.Second * 10
140+
}
141+
120142
h.acc = acc
121143
h.pool = NewPool(200, h.MaxLineSize)
122144

123-
var listener, err = net.Listen("tcp", h.ServiceAddress)
145+
tlsConf := h.getTLSConfig()
146+
147+
server := &http.Server{
148+
Addr: h.ServiceAddress,
149+
Handler: h,
150+
ReadTimeout: h.ReadTimeout.Duration,
151+
WriteTimeout: h.WriteTimeout.Duration,
152+
TLSConfig: tlsConf,
153+
}
154+
155+
var err error
156+
var listener net.Listener
157+
if tlsConf != nil {
158+
listener, err = tls.Listen("tcp", h.ServiceAddress, tlsConf)
159+
} else {
160+
listener, err = net.Listen("tcp", h.ServiceAddress)
161+
}
124162
if err != nil {
125163
return err
126164
}
@@ -130,7 +168,7 @@ func (h *HTTPListener) Start(acc telegraf.Accumulator) error {
130168
h.wg.Add(1)
131169
go func() {
132170
defer h.wg.Done()
133-
h.httpListen()
171+
server.Serve(h.listener)
134172
}()
135173

136174
log.Printf("I! Started HTTP listener service on %s\n", h.ServiceAddress)
@@ -149,27 +187,6 @@ func (h *HTTPListener) Stop() {
149187
log.Println("I! Stopped HTTP listener service on ", h.ServiceAddress)
150188
}
151189

152-
// httpListen sets up an http.Server and calls server.Serve.
153-
// like server.Serve, httpListen will always return a non-nil error, for this
154-
// reason, the error returned should probably be ignored.
155-
// see https://golang.org/pkg/net/http/#Server.Serve
156-
func (h *HTTPListener) httpListen() error {
157-
if h.ReadTimeout.Duration < time.Second {
158-
h.ReadTimeout.Duration = time.Second * 10
159-
}
160-
if h.WriteTimeout.Duration < time.Second {
161-
h.WriteTimeout.Duration = time.Second * 10
162-
}
163-
164-
var server = http.Server{
165-
Handler: h,
166-
ReadTimeout: h.ReadTimeout.Duration,
167-
WriteTimeout: h.WriteTimeout.Duration,
168-
}
169-
170-
return server.Serve(h.listener)
171-
}
172-
173190
func (h *HTTPListener) ServeHTTP(res http.ResponseWriter, req *http.Request) {
174191
h.RequestsRecv.Incr(1)
175192
defer h.RequestsServed.Incr(1)
@@ -327,6 +344,38 @@ func badRequest(res http.ResponseWriter) {
327344
res.Write([]byte(`{"error":"http: bad request"}`))
328345
}
329346

347+
func (h *HTTPListener) getTLSConfig() *tls.Config {
348+
tlsConf := &tls.Config{
349+
InsecureSkipVerify: false,
350+
Renegotiation: tls.RenegotiateNever,
351+
}
352+
353+
if len(h.TlsCert) == 0 || len(h.TlsKey) == 0 {
354+
return nil
355+
}
356+
357+
cert, err := tls.LoadX509KeyPair(h.TlsCert, h.TlsKey)
358+
if err != nil {
359+
return nil
360+
}
361+
tlsConf.Certificates = []tls.Certificate{cert}
362+
363+
if h.TlsAllowedCacerts != nil {
364+
tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
365+
clientPool := x509.NewCertPool()
366+
for _, ca := range h.TlsAllowedCacerts {
367+
c, err := ioutil.ReadFile(ca)
368+
if err != nil {
369+
continue
370+
}
371+
clientPool.AppendCertsFromPEM(c)
372+
}
373+
tlsConf.ClientCAs = clientPool
374+
}
375+
376+
return tlsConf
377+
}
378+
330379
func init() {
331380
inputs.Add("http_listener", func() telegraf.Input {
332381
return &HTTPListener{

0 commit comments

Comments
 (0)