Skip to content

Commit

Permalink
Deprecate apm-server apikey
Browse files Browse the repository at this point in the history
Emit a deprecation notice to stderr for every
`apm-server apikey` command.

JSON errors are now sent to stdout, so they can
be parsed independently of the notice.
  • Loading branch information
axw committed Oct 28, 2022
1 parent 6e75463 commit bfca99b
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 19 deletions.
1 change: 1 addition & 0 deletions changelogs/head.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ https://github.com/elastic/apm-server/compare/8.5\...main[View commits]

[float]
==== Deprecations
- `apm-server apikey` commands have been deprecated. API Keys should be managed through Kibana or the Elasticsearch REST API {pull}9446[9446]

[float]
==== Bug fixes
Expand Down
2 changes: 2 additions & 0 deletions docs/legacy/copied-from-beats/docs/command-reference.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ ifdef::apm-server[]

experimental::[]

deprecated::[8.6.0, Users should create API Keys through {kib} or the {es} REST API. See <<api-key-legacy>>.]

Communication between APM agents and APM Server now supports sending an
<<api-key-legacy,API Key in the Authorization header>>.
APM Server provides an `apikey` command that can create, verify, invalidate,
Expand Down
2 changes: 2 additions & 0 deletions docs/legacy/secure-communication-agents.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ API keys can also be created and validated outside of {kib}:
[float]
==== APM Server API key workflow

deprecated::[8.6.0, Users should create API Keys through {kib} or the {es} REST API]

APM Server provides a command line interface for creating, retrieving, invalidating, and verifying API keys.
Keys created using this method can only be used for communication with APM Server.

Expand Down
21 changes: 15 additions & 6 deletions internal/beatcmd/apikey.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,22 @@ import (
es "github.com/elastic/apm-server/internal/elasticsearch"
)

const apikeyDeprecationNotice = `NOTE: "apm-server apikey" is deprecated, and will be removed in a future release.
See https://www.elastic.co/guide/en/apm/guide/current/api-key.html for managing API Keys.`

func genApikeyCmd() *cobra.Command {

short := "Manage API Keys for communication between APM agents and server"
short := "Manage API Keys for communication between APM agents and server (deprecated)"
apikeyCmd := cobra.Command{
Use: "apikey",
Short: short,
Long: short + `.
Most operations require the "manage_api_key" cluster privilege. Ensure to configure "apm-server.api_key.*" or
"output.elasticsearch.*" appropriately. APM Server will create security privileges for the "apm" application;
you can freely query them. If you modify or delete apm privileges, APM Server might reject all requests.
Check the Elastic Security API documentation for details.`,
Check the Elastic Security API documentation for details.
` + apikeyDeprecationNotice,
}

apikeyCmd.AddCommand(
Expand Down Expand Up @@ -195,13 +200,17 @@ type cobraRunFunc func(cmd *cobra.Command, args []string)

func makeAPIKeyRun(json *bool, f apikeyRunFunc) cobraRunFunc {
return func(cmd *cobra.Command, args []string) {
var failed bool
client, config, err := bootstrap()
if err != nil {
failed = true
printErr(err, *json)
os.Exit(1)
}
if err := f(client, config, args); err != nil {
} else if err := f(client, config, args); err != nil {
failed = true
printErr(err, *json)
}
fmt.Fprintf(os.Stderr, "\n%s\n", apikeyDeprecationNotice)
if failed {
os.Exit(1)
}
}
Expand Down Expand Up @@ -492,7 +501,7 @@ func printErr(err error, asJSON bool) {
Error: err.Error(),
}, "", "\t")
}
fmt.Fprintln(os.Stderr, string(data))
fmt.Fprintln(os.Stdout, string(data))
} else {
fmt.Fprintln(os.Stderr, err.Error())
}
Expand Down
26 changes: 13 additions & 13 deletions systemtest/apikeycmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func TestAPIKeyCreate(t *testing.T) {
defer systemtest.InvalidateAPIKeys(t)

cmd := apiKeyCommand("create", "--name", t.Name(), "--json")
out, err := cmd.CombinedOutput()
out, err := cmd.Output()
require.NoError(t, err)

attrs := decodeJSONMap(t, bytes.NewReader(out))
Expand Down Expand Up @@ -99,7 +99,7 @@ func TestAPIKeyCreateExpiration(t *testing.T) {
defer systemtest.InvalidateAPIKeys(t)

cmd := apiKeyCommand("create", "--name", t.Name(), "--json", "--expiration=1d")
out, err := cmd.CombinedOutput()
out, err := cmd.Output()
require.NoError(t, err)

attrs := decodeJSONMap(t, bytes.NewReader(out))
Expand All @@ -115,7 +115,7 @@ func TestAPIKeyCreateInvalidUser(t *testing.T) {
cfg.Output.Elasticsearch.Password = "changeme"

cmd := apiKeyCommandConfig(cfg, "create", "--name", t.Name(), "--json")
out, err := cmd.CombinedOutput()
out, err := cmd.Output()
require.Error(t, err)
attrs := decodeJSONMap(t, bytes.NewReader(out))
assert.Regexp(t, username+` is missing the following requested privilege\(s\): .*`, attrs["error"])
Expand All @@ -129,7 +129,7 @@ func TestAPIKeyInvalidateName(t *testing.T) {
var clients []*estest.Client
for i := 0; i < 2; i++ {
cmd := apiKeyCommand("create", "--name", t.Name(), "--json")
out, err := cmd.CombinedOutput()
out, err := cmd.Output()
require.NoError(t, err)

attrs := decodeJSONMap(t, bytes.NewReader(out))
Expand All @@ -139,7 +139,7 @@ func TestAPIKeyInvalidateName(t *testing.T) {
}

cmd := apiKeyCommand("invalidate", "--name", t.Name(), "--json")
out, err := cmd.CombinedOutput()
out, err := cmd.Output()
require.NoError(t, err)

result := decodeJSONMap(t, bytes.NewReader(out))
Expand All @@ -156,7 +156,7 @@ func TestAPIKeyInvalidateID(t *testing.T) {
defer systemtest.InvalidateAPIKeys(t)

cmd := apiKeyCommand("create", "--json")
out, err := cmd.CombinedOutput()
out, err := cmd.Output()
require.NoError(t, err)
attrs := decodeJSONMap(t, bytes.NewReader(out))

Expand All @@ -166,7 +166,7 @@ func TestAPIKeyInvalidateID(t *testing.T) {
// NOTE(axw) it is important to use "--id=<id>" rather than "--id" <id>,
// as API keys may begin with a hyphen and be interpreted as flags.
cmd = apiKeyCommand("invalidate", "--json", "--id="+attrs["id"].(string))
out, err = cmd.CombinedOutput()
out, err = cmd.Output()
require.NoError(t, err)
result := decodeJSONMap(t, bytes.NewReader(out))

Expand All @@ -180,13 +180,13 @@ func TestAPIKeyVerify(t *testing.T) {
defer systemtest.InvalidateAPIKeys(t)

cmd := apiKeyCommand("create", "--name", t.Name(), "--json", "--ingest", "--agent-config")
out, err := cmd.CombinedOutput()
out, err := cmd.Output()
require.NoError(t, err)
attrs := decodeJSONMap(t, bytes.NewReader(out))
credentials := attrs["credentials"].(string)

cmd = apiKeyCommand("verify", "--json", "--credentials="+credentials)
out, err = cmd.CombinedOutput()
out, err = cmd.Output()
require.NoError(t, err)
attrs = decodeJSONMap(t, bytes.NewReader(out))
assert.Equal(t, map[string]interface{}{
Expand All @@ -196,7 +196,7 @@ func TestAPIKeyVerify(t *testing.T) {
}, attrs)

cmd = apiKeyCommand("verify", "--json", "--credentials="+credentials, "--ingest")
out, err = cmd.CombinedOutput()
out, err = cmd.Output()
require.NoError(t, err)
attrs = decodeJSONMap(t, bytes.NewReader(out))
assert.Equal(t, map[string]interface{}{"event:write": true}, attrs)
Expand All @@ -209,7 +209,7 @@ func TestAPIKeyInfo(t *testing.T) {
var ids []string
for i := 0; i < 2; i++ {
cmd := apiKeyCommand("create", "--name", t.Name(), "--json", "--ingest", "--agent-config")
out, err := cmd.CombinedOutput()
out, err := cmd.Output()
require.NoError(t, err)
attrs := decodeJSONMap(t, bytes.NewReader(out))
ids = append(ids, attrs["id"].(string))
Expand All @@ -224,7 +224,7 @@ func TestAPIKeyInfo(t *testing.T) {
}

cmd := apiKeyCommand("info", "--json", "--id="+ids[0])
out, err := cmd.CombinedOutput()
out, err := cmd.Output()
require.NoError(t, err)
err = json.Unmarshal(out, &result)
require.NoError(t, err)
Expand All @@ -235,7 +235,7 @@ func TestAPIKeyInfo(t *testing.T) {

result.APIKeys = nil
cmd = apiKeyCommand("info", "--json", "--name="+t.Name())
out, err = cmd.CombinedOutput()
out, err = cmd.Output()
require.NoError(t, err)
err = json.Unmarshal(out, &result)
require.NoError(t, err)
Expand Down

0 comments on commit bfca99b

Please sign in to comment.