From 12f95f06e01eb7fa6561bc5bfd6bf5395c19504d Mon Sep 17 00:00:00 2001
From: Brandon Morelli <brandon.morelli@elastic.co>
Date: Tue, 8 Sep 2020 19:28:16 -0700
Subject: [PATCH] docs: Add required privileges for using API Keys (#4130)

Co-authored-by: Andrew Wilkins <axwalk@gmail.com>
---
 .../docs/command-reference.asciidoc           |  23 +--
 docs/feature-roles.asciidoc                   |  78 +++++++++-
 docs/secure-communication-agents.asciidoc     | 145 ++++++++++++++++--
 3 files changed, 211 insertions(+), 35 deletions(-)

diff --git a/docs/copied-from-beats/docs/command-reference.asciidoc b/docs/copied-from-beats/docs/command-reference.asciidoc
index 4a600650209..a00a2baed24 100644
--- a/docs/copied-from-beats/docs/command-reference.asciidoc
+++ b/docs/copied-from-beats/docs/command-reference.asciidoc
@@ -156,18 +156,7 @@ Create an API Key with the specified privilege(s). No required flags.
 +
 The user requesting to create an API Key needs to have APM privileges used by the APM Server.
 A superuser, by default, has these privileges. For other users,
-you can create them. Create a role that is then assigned to the user:
-+
-["source","sh",subs="attributes"]
-----
-PUT /_security/role/apm-privileges {
-	"applications": [{
-	  "application": "apm",
-	  "privileges": ["sourcemap:write", "event:write", "config_agent:read"],
-	  "resources": ["*"]
-	}]
-}
-----
+you can create them. See <<privileges-api-key,create an API key user>> for required privileges.
 
 *`info`*::
 Query API Key(s). `--id` or `--name` required.
@@ -252,7 +241,7 @@ the credentials required by your cloud service provider.
 ----
 
 *`FUNCTION_NAME`*::
-Specifies the name of the function to deploy.  
+Specifies the name of the function to deploy.
 
 *FLAGS*
 
@@ -498,7 +487,7 @@ ifeval::["{beatname_lc}"=="functionbeat"]
 [[package-command]]
 ==== `package` command
 
-{package-command-short-desc}. 
+{package-command-short-desc}.
 
 *SYNOPSIS*
 
@@ -513,7 +502,7 @@ ifeval::["{beatname_lc}"=="functionbeat"]
 Shows help for the `package` command.
 
 *`-o, --output`*::
-Specifies the full path pattern to use when creating the packages. 
+Specifies the full path pattern to use when creating the packages.
 
 {global-flags}
 
@@ -538,7 +527,7 @@ the credentials required by your cloud service provider.
 ----
 
 *`FUNCTION_NAME`*::
-Specifies the name of the function to remove.  
+Specifies the name of the function to remove.
 
 *FLAGS*
 
@@ -941,7 +930,7 @@ the credentials required by your cloud service provider.
 ----
 
 *`FUNCTION_NAME`*::
-Specifies the name of the function to update.  
+Specifies the name of the function to update.
 
 *FLAGS*
 
diff --git a/docs/feature-roles.asciidoc b/docs/feature-roles.asciidoc
index 93355894e4f..559a48fa48f 100644
--- a/docs/feature-roles.asciidoc
+++ b/docs/feature-roles.asciidoc
@@ -9,10 +9,11 @@ requirements and the minimum privileges required to use specific features.
 Typically, you need to create the following separate roles:
 
 * <<privileges-to-setup-beats,Setup role>>: To set up index templates and
-other dependencies
-* <<privileges-to-publish-monitoring,Monitoring role>>: One for sending monitoring
-information, and another for viewing it
+other dependencies.
 * <<privileges-to-publish-events,Writer role>>: To publish events collected by {beatname_uc}.
+* <<privileges-to-publish-monitoring,Monitoring role>>: One for sending monitoring
+information, and another for viewing it.
+* <<privileges-api-key,API key role>>: To create and manage API keys.
 * <<privileges-agent-central-config,Central configuration management role>>: To view
 APM Agent central configurations.
 
@@ -368,6 +369,77 @@ need to view monitoring data for {beatname_uc}:
 |Grants access to monitoring indices for {beatname_uc}
 |====
 
+////
+***********************************  ***********************************
+***********************************  ***********************************
+////
+
+[[privileges-api-key]]
+=== Grant privileges and roles needed for API key management
+
+++++
+<titleabbrev>Create an _API key_ user</titleabbrev>
+++++
+
+You can configure <<api-key,API keys>> to authorize requests to APM Server.
+To create an APM Server user with the required privileges for creating and managing API keys:
+
+. Create an **API key role**, called something like `apm_api_key`,
+that has the following `cluster` level privileges:
++
+[options="header"]
+|====
+| Privilege | Purpose
+
+|`manage_api_key`
+|Allow {beatname_uc} to create, retrieve, and invalidate API keys
+|====
+
+. Depending on what the **API key role** will be used for,
+also assign any or all of the following `apm` application level privileges:
++
+* To **receive Agent configuration**, assign `config_agent:read`.
+* To **ingest agent data**, assign `event:write`.
+* To **upload sourcemaps**, assign `sourcemap:write`.
+
+. Assign the **API key role** role to users that need to create and manage API keys.
+
+[float]
+[[privileges-api-key-example]]
+=== Example API key role
+
+The following example assigns the required cluster privileges,
+and all three `apm` API key application privileges to a role named `apm_api_key`:
+
+[source,kibana]
+----
+PUT _security/role/apm_api_key <1>
+{
+  "cluster": [
+    "manage_api_key" <2>
+  ],
+  "applications": [
+    {
+      "application": "apm",
+      "privileges": [
+        "sourcemap:write", <3>
+        "event:write", <4>
+        "config_agent:read" <5>
+      ],
+      "resources": [
+        "*"
+      ]
+    }
+  ]
+}
+----
+<1> `apm_api_key` is the name of the role we're assigning these privileges to. Any name can be used.
+<2> Required cluster privileges.
+<3> Required for API keys that will be used in sourcemap uploads.
+<4> Required for API keys that will be used to ingest agent events.
+<5> Required for API keys that will be used for Agent configuration.
+
+
 ////
 ***********************************  ***********************************
 ***********************************  ***********************************
diff --git a/docs/secure-communication-agents.asciidoc b/docs/secure-communication-agents.asciidoc
index a9109c31062..852f2063d0e 100644
--- a/docs/secure-communication-agents.asciidoc
+++ b/docs/secure-communication-agents.asciidoc
@@ -32,7 +32,7 @@ include::./ssl-input.asciidoc[]
 
 experimental::[]
 
-You can configure an API key to authorize requests to the APM Server.
+You can configure API keys to authorize requests to the APM Server.
 
 NOTE: API keys are sent as plain-text,
 so they only provide security when used in combination with <<ssl-setup,SSL/TLS>>.
@@ -76,9 +76,8 @@ All other configuration options are described in <<api-key-settings>>.
 [float]
 === Create and validate an API key
 
-APM Server provides a command line interface for creating API keys.
+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 Agent/Server communication.
-The user that creates the API key will need to have the privileges they wish to give to the API key.
 
 [[create-api-key-subcommands]]
 [float]
@@ -93,17 +92,17 @@ include::{libbeat-dir}/command-reference.asciidoc[tag=apikey-subcommands]
 There are three unique privileges you can assign to each API keys.
 If privileges are not specified at creation time, the created key will have all privileges.
 
-* *Agent configuration* - Required for agents to read
+* *Agent configuration*: Required for agents to read
 {kibana-ref}/agent-configuration.html[Agent configuration remotely].
 `--agent-config` gives the `config_agent:read` privilege to the created key.
-* *Ingest* - Required for ingesting Agent events.
+* *Ingest*: Required for ingesting Agent events.
 `--ingest` gives the `event:write` privilege to the created key.
-* *Sourcemap* - Required for <<sourcemaps,uploading sourcemaps>>.
+* *Sourcemap*: Required for <<sourcemaps,uploading sourcemaps>>.
 `--sourcemap` gives the `sourcemap:write` privilege to the created key.
 
 [[create-api-key-workflow]]
 [float]
-==== API key example workflow
+==== API key workflow example
 
 Create an API key with the `create` subcommand.
 
@@ -117,13 +116,13 @@ and gives the "agent configuration" and "ingest" privileges.
 
 The response will look similar to this:
 
-[source,console-result,subs="attributes,callouts"]
+[source,console-result]
 --------------------------------------------------
 Name ........... java-001
 Expiration ..... never
 Id ............. qT4tz28B1g59zC3uAXfW
 API Key ........ rH55zKd5QT6wvs3UbbkxOA (won't be shown again)
-Credentials .... cVQ0dHoyOEIxZzVDZ3dnMzVWJia3hPQQ== (won't be shown again)
+Credentials .... cVQ0dHoyOEIxZzU5ekMzdUFYZlc6ckg1NXpLZDVRVDZ3dnMzVWJia3hPQQ== (won't be shown again)
 --------------------------------------------------
 
 You should always verify the privileges of an API key after creating it.
@@ -133,12 +132,12 @@ The following example verifies that the `java-001` API key has the "agent config
 
 ["source","sh",subs="attributes"]
 -----
-{beatname_lc} apikey verify --agent-config --ingest --credentials cVQ0dHoyOEIxZzVDZ3dnMzVWJia3hPQQ==
+{beatname_lc} apikey verify --agent-config --ingest --credentials cVQ0dHoyOEIxZzU5ekMzdUFYZlc6ckg1NXpLZDVRVDZ3dnMzVWJia3hPQQ==
 -----
 
 If the API key has the requested privileges, the response will look similar to this:
 
-[source,console-result,subs="attributes,callouts"]
+[source,console-result]
 --------------------------------------------------
 Authorized for privilege "event:write"...:          Yes
 Authorized for privilege "config_agent:read"...:    Yes
@@ -156,7 +155,7 @@ The following example invalidates the `java-001` API key.
 
 The response will look similar to this:
 
-[source,console-result,subs="attributes,callouts"]
+[source,console-result]
 --------------------------------------------------
 Invalidated keys ... qT4tz28B1g59zC3uAXfW
 Error count ........ 0
@@ -164,6 +163,120 @@ Error count ........ 0
 
 A full list of `apikey` subcommands and flags is available in the <<apikey-command,API key command reference>>.
 
+[[create-api-key-workflow-es]]
+[float]
+==== Alternate API key workflow example
+
+Instead of using the APM Server CLI, it is possible to create API keys using the Elasticsearch
+{ref}/security-api-create-api-key.html[create API key API].
+
+This example creates an API key named `java-002`:
+
+[source,kibana]
+----
+POST /_security/api_key
+{
+  "name": "java-002", <1>
+  "expiration": "1d", <2>
+  "role_descriptors": {
+    "apm": {
+      "applications": [
+        {
+          "application": "apm",
+          "privileges": ["sourcemap:write", "event:write", "config_agent:read"], <3>
+          "resources": ["*"]
+        }
+      ]
+    }
+  }
+}
+----
+<1> The name of the API key
+<2> The expiration time of the API key
+<3> Any assigned privileges
+
+The response will look similar to this:
+
+[source,console-result]
+----
+{
+  "id" : "GnrUT3QB7yZbSNxKET6d",
+  "name" : "java-002",
+  "expiration" : 1599153532262,
+  "api_key" : "RhHKisTmQ1aPCHC_TPwOvw"
+}
+----
+
+The `credential` string, which is what agents use to communicate with APM Server,
+is a base64 encoded representation of the API key's `id:api_key`.
+It can be created like this:
+
+[source,console-result]
+--------------------------------------------------
+echo -n GnrUT3QB7yZbSNxKET6d:RhHKisTmQ1aPCHC_TPwOvw | base64
+--------------------------------------------------
+
+You can verify your API key has been base64-encoded correctly with the
+{ref}/security-api-authenticate.html[Authenticate API]:
+
+["source","sh",subs="attributes"]
+-----
+curl -H "Authorization: ApiKey R0gzRWIzUUI3eVpiU054S3pYSy06bXQyQWl4TlZUeEcyUjd4cUZDS0NlUQ==" localhost:9200/_security/_authentication
+-----
+
+If the API key has been encoded correctly, you'll see a response similar to the following:
+
+[source,console-result]
+----
+{
+   "username":"1325298603",
+   "roles":[],
+   "full_name":null,
+   "email":null,
+   "metadata":{
+      "saml_nameid_format":"urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
+      "saml(http://saml.elastic-cloud.com/attributes/principal)":[
+         "1325298603"
+      ],
+      "saml_roles":[
+         "superuser"
+      ],
+      "saml_principal":[
+         "1325298603"
+      ],
+      "saml_nameid":"_7b0ab93bbdbc21d825edf7dca9879bd8d44c0be2",
+      "saml(http://saml.elastic-cloud.com/attributes/roles)":[
+         "superuser"
+      ]
+   },
+   "enabled":true,
+   "authentication_realm":{
+      "name":"_es_api_key",
+      "type":"_es_api_key"
+   },
+   "lookup_realm":{
+      "name":"_es_api_key",
+      "type":"_es_api_key"
+   }
+}
+----
+
+You can then use the APM Server CLI to verify that the API key has the requested privileges:
+
+["source","sh",subs="attributes"]
+-----
+{beatname_lc} apikey verify --credentials R25yVVQzUUI3eVpiU054S0VUNmQ6UmhIS2lzVG1RMWFQQ0hDX1RQd092dw==
+-----
+
+If the API key has the requested privileges, the response will look similar to this:
+
+[source,console-result]
+----
+Authorized for privilege "config_agent:read"...:  Yes
+Authorized for privilege "event:write"...:        Yes
+Authorized for privilege "sourcemap:write"...:    Yes
+----
+
 [[set-api-key]]
 [float]
 === Set the API key in your APM Agents
@@ -186,16 +299,18 @@ You can specify the following options in the `apm-server.api_key.*` section of t
 +{beatname_lc}.yml+ configuration file.
 They apply to API key communication between the APM Server and APM Agents.
 
-These are different from the API key settings used for the Elasticsearch output and monitoring.
+NOTE: These settings are different from the API key settings used for Elasticsearch output and monitoring.
 
 [float]
 ===== `enabled`
 
 Enable API key authorization by setting `enabled` to `true`.
-Agents will include a valid API key in the following format: `Authorization: ApiKey <token>`.
-The key must be the base64 encoded representation of the API key's `id:name`.
 By default, `enabled` is set to `false`, and API key support is disabled.
 
+TIP: Not using Elastic APM agents?
+When enabled, third-party APM agents must include a valid API key in the following format:
+`Authorization: ApiKey <token>`. The key must be the base64 encoded representation of the API key's `id:name`.
+
 [float]
 ===== `limit`