diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index d8138e76d..3b2ff57af 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -23,6 +23,8 @@ endif::[] https://github.com/elastic/apm-agent-go/compare/v1.6.0...master[View commits] + - Add support for API Key auth {pull}698[(#698)] + [[release-notes-1.x]] === Go Agent version 1.x diff --git a/docs/configuration.asciidoc b/docs/configuration.asciidoc index 27601b691..01d073ab8 100644 --- a/docs/configuration.asciidoc +++ b/docs/configuration.asciidoc @@ -19,9 +19,10 @@ To simplify development and testing, the agent defaults to sending data to the Elastic APM Server at `http://localhost:8200`. To send data to an alternative location, you must configure <>. Depending on the configuration -of your server, you may also need to set <> -and <>. All other -variables have usable defaults. +of your server, you may also need to set <>, +<>, and +<>. All other variables +have usable defaults. // end::setup-config[] [float] @@ -106,6 +107,25 @@ WARNING: the secret token is sent as plain-text in every request to the server, should also secure your communications using HTTPS. Unless you do so, your secret token could be observed by an attacker. +[float] +[[config-api-key]] +==== `ELASTIC_APM_API_KEY` + +[options="header"] +|============ +| Environment | Default | Example +| `ELASTIC_APM_API_KEY` | | "A base64-encoded string" +|============ + +This base64-encoded string is used to ensure that only your agents can send data to your APM server. +You must have created the API key using the APM server command line tool. Please see the APM server +documentation for details on how to do that. + +NOTE: This feature is fully supported in the APM Server versions >= 7.6. + +WARNING: the API Key is sent as plain-text in every request to the server, so you should also secure +your communications using HTTPS. Unless you do so, your API Key could be observed by an attacker. + [float] [[config-service-name]] === `ELASTIC_APM_SERVICE_NAME` diff --git a/features/api_key.feature b/features/api_key.feature new file mode 100644 index 000000000..af005d4dc --- /dev/null +++ b/features/api_key.feature @@ -0,0 +1,18 @@ +Feature: Api Key + + Scenario: A configured api key is sent in the Authorization header + Given an agent + When an api key is set to 'RTNxMjlXNEJt' in the config + Then the Authorization header is 'ApiKey RTNxMjlXNEJt' + + Scenario: A configured api key takes precedence over a secret token + Given an agent + When an api key is set in the config + And a secret_token is set in the config + Then the api key is sent in the Authorization header + + Scenario: A configured secret token is sent if no api key is configured + Given an agent + When a secret_token is set in the config + And an api key is not set in the config + Then the secret token is sent in the Authorization header diff --git a/go.mod b/go.mod index 553b9dbdc..ea9cd75b0 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,7 @@ module go.elastic.co/apm require ( + github.com/DATA-DOG/godog v0.7.13 github.com/armon/go-radix v1.0.0 github.com/davecgh/go-spew v1.1.1 // indirect github.com/elastic/go-sysinfo v1.1.1 diff --git a/go.sum b/go.sum index e258e23aa..50a14b707 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/DATA-DOG/godog v0.7.13 h1:JmgpKcra7Vf3yzI9vPsWyoQRx13tyKziHtXWDCUUgok= +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/internal/apmgodog/formatter.go b/internal/apmgodog/formatter.go new file mode 100644 index 000000000..80c16f280 --- /dev/null +++ b/internal/apmgodog/formatter.go @@ -0,0 +1,118 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmgodog + +import ( + "fmt" + "io/ioutil" + "testing" + + "github.com/DATA-DOG/godog" + "github.com/DATA-DOG/godog/colors" + "github.com/DATA-DOG/godog/gherkin" +) + +// Run runs the Gherkin feature files specified in paths as Go subtests. +func Run(t *testing.T, paths []string) { + initContext := func(s *godog.Suite) { + var commands chan command + var scenarioFailed bool + s.BeforeFeature(func(f *gherkin.Feature) { + commands = make(chan command) + go runCommands(t, commands) + startTest(commands, f.Name) + }) + s.AfterFeature(func(f *gherkin.Feature) { + endTest(commands, nil) + close(commands) + }) + s.BeforeScenario(func(s interface{}) { + scenarioFailed = false + switch s := s.(type) { + case *gherkin.Scenario: + startTest(commands, s.Name) + case *gherkin.ScenarioOutline: + startTest(commands, s.Name) + } + }) + s.AfterScenario(func(_ interface{}, err error) { + endTest(commands, err) + }) + s.BeforeStep(func(step *gherkin.Step) { + if scenarioFailed { + fmt.Printf(colors.Yellow(" %s%s\n"), step.Keyword, step.Text) + } + }) + s.AfterStep(func(step *gherkin.Step, err error) { + if err != nil { + scenarioFailed = true + fmt.Printf(colors.Red(" %s%s (%s)\n"), step.Keyword, step.Text, err) + } else { + fmt.Printf(colors.Cyan(" %s%s\n"), step.Keyword, step.Text) + } + }) + InitContext(s) + } + + godog.RunWithOptions("godog", initContext, godog.Options{ + Format: "events", // must pick one, this will do + Paths: []string{"."}, + Output: ioutil.Discard, + }) +} + +func startTest(commands chan command, name string) { + commands <- func(t *testing.T) error { + t.Run(name, func(t *testing.T) { + runCommands(t, commands) + }) + return nil + } +} + +func endTest(commands chan command, err error) { + commands <- func(t *testing.T) error { + if err != nil { + return err + } + return done{} + } +} + +func runCommands(t *testing.T, commands chan command) { + for { + cmd, ok := <-commands + if !ok { + return + } + err := cmd(t) + switch err.(type) { + case nil: + case done: + return + default: + t.Fatal(err) + } + } +} + +type command func(t *testing.T) error + +type done struct{} + +func (done) Error() string { return "done" } diff --git a/internal/apmgodog/suitecontext.go b/internal/apmgodog/suitecontext.go new file mode 100644 index 000000000..f0338c23d --- /dev/null +++ b/internal/apmgodog/suitecontext.go @@ -0,0 +1,122 @@ +// Licensed to Elasticsearch B.V. under one or more contributor +// license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright +// ownership. Elasticsearch B.V. licenses this file to you under +// the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package apmgodog + +import ( + "fmt" + "net/http" + "net/http/httptest" + "os" + "strings" + + "github.com/DATA-DOG/godog" + + "go.elastic.co/apm" + "go.elastic.co/apm/transport" +) + +const ( + aSecretToken = "a_secret_token" + anAPIKey = "an_api_key" +) + +type featureContext struct { + apiKey string + secretToken string +} + +// InitContext initialises a godoc.Suite with step definitions. +func InitContext(s *godog.Suite) { + c := &featureContext{} + s.BeforeScenario(func(interface{}) { c.reset() }) + + s.Step("^an agent$", c.anAgent) + s.Step("^an api key is not set in the config$", func() error { return nil }) + s.Step("^an api key is set in the config$", func() error { return c.setAPIKey(anAPIKey) }) + s.Step("^an api key is set to '(.*)' in the config$", c.setAPIKey) + s.Step("^a secret_token is set in the config$", func() error { return c.setSecretToken(aSecretToken) }) + s.Step("^the Authorization header is '(.*)'$", c.checkAuthorizationHeader) + s.Step("^the secret token is sent in the Authorization header$", c.secretTokenSentInAuthorizationHeader) + s.Step("^the api key is sent in the Authorization header$", c.apiKeySentInAuthorizationHeader) +} + +func (c *featureContext) reset() { + c.apiKey = "" + c.secretToken = "" + for _, k := range os.Environ() { + if strings.HasPrefix(k, "ELASTIC_APM") { + os.Unsetenv(k) + } + } +} + +func (c *featureContext) anAgent() error { + // No-op; we create the tracer as needed to test steps. + return nil +} + +func (c *featureContext) setAPIKey(v string) error { + c.apiKey = v + return nil +} + +func (c *featureContext) setSecretToken(v string) error { + c.secretToken = v + return nil +} + +func (c *featureContext) secretTokenSentInAuthorizationHeader() error { + return c.checkAuthorizationHeader("Bearer " + c.secretToken) +} + +func (c *featureContext) apiKeySentInAuthorizationHeader() error { + return c.checkAuthorizationHeader("ApiKey " + c.apiKey) +} + +func (c *featureContext) checkAuthorizationHeader(expected string) error { + var authHeader []string + var h http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { + authHeader = r.Header["Authorization"] + } + server := httptest.NewServer(h) + defer server.Close() + + os.Setenv("ELASTIC_APM_SECRET_TOKEN", c.secretToken) + os.Setenv("ELASTIC_APM_API_KEY", c.apiKey) + os.Setenv("ELASTIC_APM_SERVER_URL", server.URL) + if _, err := transport.InitDefault(); err != nil { + return err + } + + tracer, err := apm.NewTracer("godog", "") + if err != nil { + return err + } + defer tracer.Close() + + tracer.StartTransaction("name", "type").End() + tracer.Flush(nil) + + if n := len(authHeader); n != 1 { + return fmt.Errorf("got %d Authorization headers, expected 1", n) + } + if authHeader[0] != expected { + return fmt.Errorf("got Authorization header value %q, expected %q", authHeader, expected) + } + return nil +} diff --git a/internal/tracecontexttest/go.sum b/internal/tracecontexttest/go.sum index d1d392555..b60add410 100644 --- a/internal/tracecontexttest/go.sum +++ b/internal/tracecontexttest/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmbeego/go.sum b/module/apmbeego/go.sum index 09e5de6d0..af5f68876 100644 --- a/module/apmbeego/go.sum +++ b/module/apmbeego/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= diff --git a/module/apmchi/go.sum b/module/apmchi/go.sum index a608c1f82..2c906c16e 100644 --- a/module/apmchi/go.sum +++ b/module/apmchi/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmecho/go.sum b/module/apmecho/go.sum index dc2f15a8f..d89365669 100644 --- a/module/apmecho/go.sum +++ b/module/apmecho/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmechov4/go.sum b/module/apmechov4/go.sum index be32820e8..90873f490 100644 --- a/module/apmechov4/go.sum +++ b/module/apmechov4/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmelasticsearch/go.sum b/module/apmelasticsearch/go.sum index c80fd22fc..1a598a39b 100644 --- a/module/apmelasticsearch/go.sum +++ b/module/apmelasticsearch/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmelasticsearch/internal/integration/go.sum b/module/apmelasticsearch/internal/integration/go.sum index 22770325b..c164c91f9 100644 --- a/module/apmelasticsearch/internal/integration/go.sum +++ b/module/apmelasticsearch/internal/integration/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmgin/go.sum b/module/apmgin/go.sum index 52c8b9b0c..58480c4e5 100644 --- a/module/apmgin/go.sum +++ b/module/apmgin/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmgocql/go.sum b/module/apmgocql/go.sum index db0f0e53b..c68b7d7bd 100644 --- a/module/apmgocql/go.sum +++ b/module/apmgocql/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= diff --git a/module/apmgokit/go.sum b/module/apmgokit/go.sum index 140a729d9..f540cd7d7 100644 --- a/module/apmgokit/go.sum +++ b/module/apmgokit/go.sum @@ -1,4 +1,5 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= diff --git a/module/apmgometrics/go.sum b/module/apmgometrics/go.sum index 3f354e8ed..e60e1bf72 100644 --- a/module/apmgometrics/go.sum +++ b/module/apmgometrics/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmgopg/go.sum b/module/apmgopg/go.sum index 12de61e29..015b3b80e 100644 --- a/module/apmgopg/go.sum +++ b/module/apmgopg/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmgoredis/go.sum b/module/apmgoredis/go.sum index 513921940..c6b5eee99 100644 --- a/module/apmgoredis/go.sum +++ b/module/apmgoredis/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmgorilla/go.sum b/module/apmgorilla/go.sum index 8ccd27ecf..664aab6fb 100644 --- a/module/apmgorilla/go.sum +++ b/module/apmgorilla/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmgorm/go.sum b/module/apmgorm/go.sum index d6da1650f..7929eb0c6 100644 --- a/module/apmgorm/go.sum +++ b/module/apmgorm/go.sum @@ -6,6 +6,7 @@ cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSR cloud.google.com/go v0.40.0 h1:FjSY7bOj+WzJe6TZRVtXI2b9kAYvtNg4lMbcH2+MUkk= cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= diff --git a/module/apmgrpc/go.sum b/module/apmgrpc/go.sum index cae40ab45..666b19bf3 100644 --- a/module/apmgrpc/go.sum +++ b/module/apmgrpc/go.sum @@ -1,4 +1,5 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= diff --git a/module/apmhttp/go.sum b/module/apmhttp/go.sum index c80fd22fc..1a598a39b 100644 --- a/module/apmhttp/go.sum +++ b/module/apmhttp/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmhttprouter/go.sum b/module/apmhttprouter/go.sum index 119ccf188..c08cc9bbe 100644 --- a/module/apmhttprouter/go.sum +++ b/module/apmhttprouter/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmlambda/go.sum b/module/apmlambda/go.sum index 8c540a7ec..f1bf88c55 100644 --- a/module/apmlambda/go.sum +++ b/module/apmlambda/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-lambda-go v1.8.0 h1:YMCzi9FP7MNVVj9AkGpYyaqh/mvFOjhqiDtnNlWtKTg= diff --git a/module/apmlogrus/go.sum b/module/apmlogrus/go.sum index 38b26d86c..34f7138d8 100644 --- a/module/apmlogrus/go.sum +++ b/module/apmlogrus/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmmongo/go.sum b/module/apmmongo/go.sum index a2a35fc0a..6bf14919d 100644 --- a/module/apmmongo/go.sum +++ b/module/apmmongo/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmnegroni/go.sum b/module/apmnegroni/go.sum index 06ae7a245..fe9d5c087 100644 --- a/module/apmnegroni/go.sum +++ b/module/apmnegroni/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmot/go.sum b/module/apmot/go.sum index 4b2798e8c..ef7cda916 100644 --- a/module/apmot/go.sum +++ b/module/apmot/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmprometheus/go.sum b/module/apmprometheus/go.sum index c82c2ee51..faa826c00 100644 --- a/module/apmprometheus/go.sum +++ b/module/apmprometheus/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= diff --git a/module/apmredigo/go.sum b/module/apmredigo/go.sum index 843b0d634..2f5181b10 100644 --- a/module/apmredigo/go.sum +++ b/module/apmredigo/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmrestful/go.sum b/module/apmrestful/go.sum index 6ad104c20..58c338847 100644 --- a/module/apmrestful/go.sum +++ b/module/apmrestful/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmsql/go.sum b/module/apmsql/go.sum index 1b065ee15..b77c7105f 100644 --- a/module/apmsql/go.sum +++ b/module/apmsql/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmzap/go.sum b/module/apmzap/go.sum index 539281844..a7957b197 100644 --- a/module/apmzap/go.sum +++ b/module/apmzap/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/module/apmzerolog/go.sum b/module/apmzerolog/go.sum index d5dddd966..26588d44a 100644 --- a/module/apmzerolog/go.sum +++ b/module/apmzerolog/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= diff --git a/scripts/genmod/go.sum b/scripts/genmod/go.sum index ed8465fcd..a94ce426e 100644 --- a/scripts/genmod/go.sum +++ b/scripts/genmod/go.sum @@ -1,3 +1,4 @@ +github.com/DATA-DOG/godog v0.7.13/go.mod h1:z2OZ6a3X0/YAKVqLfVzYBwFt3j6uSt3Xrqa7XTtcQE0= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/testmain_test.go b/testmain_test.go index 08c0bbdd9..ff840151f 100644 --- a/testmain_test.go +++ b/testmain_test.go @@ -29,6 +29,7 @@ import ( "github.com/stretchr/testify/require" "go.elastic.co/apm/apmtest" + "go.elastic.co/apm/internal/apmgodog" "go.elastic.co/apm/model" "go.elastic.co/fastjson" ) @@ -47,6 +48,10 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } +func TestFeatures(t *testing.T) { + apmgodog.Run(t, []string{"."}) +} + func getSubprocessMetadata(t *testing.T, env ...string) (*model.System, *model.Process, *model.Service, model.StringMap) { cmd := exec.Command(os.Args[0], "-dump-metadata") cmd.Env = append(os.Environ(), env...) diff --git a/transport/http.go b/transport/http.go index 432d14add..d084a51d4 100644 --- a/transport/http.go +++ b/transport/http.go @@ -52,6 +52,7 @@ const ( profilePath = "/intake/v2/profile" configPath = "/config/v1/agents" + envAPIKey = "ELASTIC_APM_API_KEY" envSecretToken = "ELASTIC_APM_SECRET_TOKEN" envServerURLs = "ELASTIC_APM_SERVER_URLS" envServerURL = "ELASTIC_APM_SERVER_URL" @@ -174,7 +175,11 @@ func NewHTTPTransport() (*HTTPTransport, error) { intakeHeaders: intakeHeaders, profileHeaders: profileHeaders, } - t.SetSecretToken(os.Getenv(envSecretToken)) + if apiKey := os.Getenv(envAPIKey); apiKey != "" { + t.SetAPIKey(apiKey) + } else if secretToken := os.Getenv(envSecretToken); secretToken != "" { + t.SetSecretToken(secretToken) + } t.SetServerURL(serverURLs...) return t, nil } @@ -217,8 +222,9 @@ func (t *HTTPTransport) SetUserAgent(ua string) { } // SetSecretToken sets the Authorization header with the given secret token. -// This overrides the value specified via the ELASTIC_APM_SECRET_TOKEN -// environment variable, if any. +// +// This overrides the value specified via the ELASTIC_APM_SECRET_TOKEN or +// ELASTIC_APM_API_KEY environment variables, if either are set. func (t *HTTPTransport) SetSecretToken(secretToken string) { if secretToken != "" { t.setCommonHeader("Authorization", "Bearer "+secretToken) @@ -227,6 +233,18 @@ func (t *HTTPTransport) SetSecretToken(secretToken string) { } } +// SetAPIKey sets the Authorization header with the given API Key. +// +// This overrides the value specified via the ELASTIC_APM_SECRET_TOKEN or +// ELASTIC_APM_API_KEY environment variables, if either are set. +func (t *HTTPTransport) SetAPIKey(apiKey string) { + if apiKey != "" { + t.setCommonHeader("Authorization", "ApiKey "+apiKey) + } else { + t.deleteCommonHeader("Authorization") + } +} + func (t *HTTPTransport) setCommonHeader(key, value string) { t.configHeaders.Set(key, value) t.intakeHeaders.Set(key, value) diff --git a/transport/http_test.go b/transport/http_test.go index bf2cb19ba..bb43113b6 100644 --- a/transport/http_test.go +++ b/transport/http_test.go @@ -107,7 +107,7 @@ func TestHTTPTransportSecretToken(t *testing.T) { transport.SendStream(context.Background(), strings.NewReader("")) assert.Len(t, h.requests, 1) - assertAuthorization(t, h.requests[0], "hunter2") + assertAuthorization(t, h.requests[0], "Bearer hunter2") } func TestHTTPTransportEnvSecretToken(t *testing.T) { @@ -122,10 +122,41 @@ func TestHTTPTransportEnvSecretToken(t *testing.T) { transport.SendStream(context.Background(), strings.NewReader("")) assert.Len(t, h.requests, 1) - assertAuthorization(t, h.requests[0], "hunter2") + assertAuthorization(t, h.requests[0], "Bearer hunter2") } -func TestHTTPTransportNoSecretToken(t *testing.T) { +func TestHTTPTransportAPIKey(t *testing.T) { + var h recordingHandler + server := httptest.NewServer(&h) + defer server.Close() + defer patchEnv("ELASTIC_APM_SERVER_URLS", server.URL)() + + transport, err := transport.NewHTTPTransport() + transport.SetAPIKey("hunter2") + assert.NoError(t, err) + transport.SendStream(context.Background(), strings.NewReader("")) + + assert.Len(t, h.requests, 1) + assertAuthorization(t, h.requests[0], "ApiKey hunter2") +} + +func TestHTTPTransportEnvAPIKey(t *testing.T) { + var h recordingHandler + server := httptest.NewServer(&h) + defer server.Close() + defer patchEnv("ELASTIC_APM_SERVER_URLS", server.URL)() + defer patchEnv("ELASTIC_APM_API_KEY", "api_key_wins")() + defer patchEnv("ELASTIC_APM_SECRET_TOKEN", "secret_token_loses")() + + transport, err := transport.NewHTTPTransport() + assert.NoError(t, err) + transport.SendStream(context.Background(), strings.NewReader("")) + + assert.Len(t, h.requests, 1) + assertAuthorization(t, h.requests[0], "ApiKey api_key_wins") +} + +func TestHTTPTransportNoAuthorization(t *testing.T) { var h recordingHandler transport, server := newHTTPTransport(t, &h) defer server.Close() @@ -133,7 +164,7 @@ func TestHTTPTransportNoSecretToken(t *testing.T) { transport.SendStream(context.Background(), strings.NewReader("")) assert.Len(t, h.requests, 1) - assertAuthorization(t, h.requests[0], "") + assertAuthorization(t, h.requests[0]) } func TestHTTPTransportTLS(t *testing.T) { diff --git a/transport/util_test.go b/transport/util_test.go index 5c42a9362..3858efd41 100644 --- a/transport/util_test.go +++ b/transport/util_test.go @@ -51,18 +51,15 @@ func (h *recordingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { h.requests = append(h.requests, req) } -func assertAuthorization(t *testing.T, req *http.Request, token string) { +func assertAuthorization(t *testing.T, req *http.Request, expect ...string) { values, ok := req.Header["Authorization"] - if !ok { - if token == "" { - return - } - t.Errorf("missing Authorization header") + if ok && len(expect) == 0 { + t.Errorf("unexpected Authorization header") return } - var expect []string - if token != "" { - expect = []string{"Bearer " + token} + if !ok && len(expect) != 0 { + t.Errorf("missing Authorization header") + return } assert.Equal(t, expect, values) }