feat(http): Allow secrets in headers (#14743)

This commit is contained in:
Lars Stegman 2024-02-12 23:04:49 +01:00 committed by GitHub
parent 616ad305fe
commit 4f0ac6e155
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 46 additions and 26 deletions

View File

@ -17,8 +17,8 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
## Secret-store support ## Secret-store support
This plugin supports secrets from secret-stores for the `username`, `password` This plugin supports secrets from secret-stores for the `username`, `password`,
and `token` option. `token` and `headers` option.
See the [secret-store documentation][SECRETSTORE] for more details on how See the [secret-store documentation][SECRETSTORE] for more details on how
to use them. to use them.

View File

@ -38,9 +38,9 @@ type HTTP struct {
Token config.Secret `toml:"token"` Token config.Secret `toml:"token"`
TokenFile string `toml:"token_file"` TokenFile string `toml:"token_file"`
Headers map[string]string `toml:"headers"` Headers map[string]*config.Secret `toml:"headers"`
SuccessStatusCodes []int `toml:"success_status_codes"` SuccessStatusCodes []int `toml:"success_status_codes"`
Log telegraf.Logger `toml:"-"` Log telegraf.Logger `toml:"-"`
httpconfig.HTTPClientConfig httpconfig.HTTPClientConfig
@ -142,11 +142,19 @@ func (h *HTTP) gatherURL(acc telegraf.Accumulator, url string) error {
} }
for k, v := range h.Headers { for k, v := range h.Headers {
if strings.EqualFold(k, "host") { secret, err := v.Get()
request.Host = v if err != nil {
} else { return err
request.Header.Add(k, v)
} }
headerVal := secret.String()
if strings.EqualFold(k, "host") {
request.Host = headerVal
} else {
request.Header.Add(k, headerVal)
}
secret.Destroy()
} }
if err := h.setRequestAuth(request); err != nil { if err := h.setRequestAuth(request); err != nil {

View File

@ -3,6 +3,7 @@ package http_test
import ( import (
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"github.com/influxdata/telegraf/config"
"io" "io"
"math/rand" "math/rand"
"net" "net"
@ -82,9 +83,10 @@ func TestHTTPHeaders(t *testing.T) {
defer fakeServer.Close() defer fakeServer.Close()
address := fakeServer.URL + "/endpoint" address := fakeServer.URL + "/endpoint"
headerSecret := config.NewSecret([]byte(headerValue))
plugin := &httpplugin.HTTP{ plugin := &httpplugin.HTTP{
URLs: []string{address}, URLs: []string{address},
Headers: map[string]string{header: headerValue}, Headers: map[string]*config.Secret{header: &headerSecret},
Log: testutil.Logger{}, Log: testutil.Logger{},
} }
@ -116,7 +118,7 @@ func TestHTTPContentLengthHeader(t *testing.T) {
address := fakeServer.URL + "/endpoint" address := fakeServer.URL + "/endpoint"
plugin := &httpplugin.HTTP{ plugin := &httpplugin.HTTP{
URLs: []string{address}, URLs: []string{address},
Headers: map[string]string{}, Headers: map[string]*config.Secret{},
Body: "{}", Body: "{}",
Log: testutil.Logger{}, Log: testutil.Logger{},
} }

View File

@ -15,8 +15,8 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
## Secret-store support ## Secret-store support
This plugin supports secrets from secret-stores for the `username` and This plugin supports secrets from secret-stores for the `username`, `password`
`password` option. and `headers` option.
See the [secret-store documentation][SECRETSTORE] for more details on how See the [secret-store documentation][SECRETSTORE] for more details on how
to use them. to use them.

View File

@ -43,15 +43,15 @@ const (
) )
type HTTP struct { type HTTP struct {
URL string `toml:"url"` URL string `toml:"url"`
Method string `toml:"method"` Method string `toml:"method"`
Username config.Secret `toml:"username"` Username config.Secret `toml:"username"`
Password config.Secret `toml:"password"` Password config.Secret `toml:"password"`
Headers map[string]string `toml:"headers"` Headers map[string]*config.Secret `toml:"headers"`
ContentEncoding string `toml:"content_encoding"` ContentEncoding string `toml:"content_encoding"`
UseBatchFormat bool `toml:"use_batch_format"` UseBatchFormat bool `toml:"use_batch_format"`
AwsService string `toml:"aws_service"` AwsService string `toml:"aws_service"`
NonRetryableStatusCodes []int `toml:"non_retryable_statuscodes"` NonRetryableStatusCodes []int `toml:"non_retryable_statuscodes"`
httpconfig.HTTPClientConfig httpconfig.HTTPClientConfig
Log telegraf.Logger `toml:"-"` Log telegraf.Logger `toml:"-"`
@ -204,11 +204,20 @@ func (h *HTTP) writeMetric(reqBody []byte) error {
if h.ContentEncoding == "gzip" { if h.ContentEncoding == "gzip" {
req.Header.Set("Content-Encoding", "gzip") req.Header.Set("Content-Encoding", "gzip")
} }
for k, v := range h.Headers { for k, v := range h.Headers {
if strings.EqualFold(k, "host") { secret, err := v.Get()
req.Host = v if err != nil {
return err
} }
req.Header.Set(k, v)
headerVal := secret.String()
if strings.EqualFold(k, "host") {
req.Host = headerVal
}
req.Header.Set(k, headerVal)
secret.Destroy()
} }
resp, err := h.client.Do(req) resp, err := h.client.Do(req)

View File

@ -290,6 +290,7 @@ func TestContentType(t *testing.T) {
u, err := url.Parse("http://" + ts.Listener.Addr().String()) u, err := url.Parse("http://" + ts.Listener.Addr().String())
require.NoError(t, err) require.NoError(t, err)
headerSecret := config.NewSecret([]byte("application/json"))
tests := []struct { tests := []struct {
name string name string
plugin *HTTP plugin *HTTP
@ -306,7 +307,7 @@ func TestContentType(t *testing.T) {
name: "overwrite content_type", name: "overwrite content_type",
plugin: &HTTP{ plugin: &HTTP{
URL: u.String(), URL: u.String(),
Headers: map[string]string{"Content-Type": "application/json"}, Headers: map[string]*config.Secret{"Content-Type": &headerSecret},
}, },
expected: "application/json", expected: "application/json",
}, },