Skip to content

Commit

Permalink
[Fleet] Use ES api keys for agent authentication (#49639)
Browse files Browse the repository at this point in the history
  • Loading branch information
nchaulet authored Nov 11, 2019
1 parent 5bcca8c commit 3612b98
Show file tree
Hide file tree
Showing 70 changed files with 5,051 additions and 3,687 deletions.
2 changes: 1 addition & 1 deletion src/test_utils/kbn_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ export function createTestServers({

return {
startES: async () => {
await es.start();
await es.start(get(settings, 'es.esArgs', []));

if (['gold', 'trial'].includes(license)) {
await setupUsers({
Expand Down
2 changes: 2 additions & 0 deletions x-pack/legacy/plugins/fleet/common/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ export { INDEX_NAMES } from './index_names';
export { PLUGIN } from './plugin';
export * from './agent';
export const BASE_PATH = '/fleet';

export const DEFAULT_POLICY_ID = 'default';
2 changes: 1 addition & 1 deletion x-pack/legacy/plugins/fleet/common/return_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export interface ReturnTypeCheckin extends BaseReturnType {
type: string;
data?: object;
}>;
policy: { [k: string]: any };
policy: { [k: string]: any } | null;
}

export interface ReturnTypeBulkDelete extends BaseReturnType {
Expand Down
3 changes: 3 additions & 0 deletions x-pack/legacy/plugins/fleet/common/types/domain_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import * as t from 'io-ts';
import { RuntimeAgent, RuntimeAgentAction } from '../../server/repositories/agents/types';
import { RuntimeAgentEvent } from '../../server/repositories/agent_events/types';
import { EnrollmentApiKey } from '../../server/repositories/enrollment_api_keys/types';

// Here we create the runtime check for a generic, unknown beat config type.
// We can also pass in optional params to create spacific runtime checks that
Expand Down Expand Up @@ -35,6 +36,8 @@ export type Agent = t.TypeOf<typeof RuntimeAgent>;
export type AgentAction = t.TypeOf<typeof RuntimeAgentAction>;
export type AgentEvent = t.TypeOf<typeof RuntimeAgentEvent>;

export type EnrollmentApiKey = EnrollmentApiKey;

export type PolicyUpdatedEvent =
| {
type: 'created';
Expand Down
3 changes: 2 additions & 1 deletion x-pack/legacy/plugins/fleet/dev_docs/api/agents_checkin.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Report current state of a Fleet agent.

## Headers

- `kbn-fleet-access-token` (Required, string) A fleet agent access token.
- `Authorization` (Required, string) A valid fleet access api key..

## Request body

Expand All @@ -25,6 +25,7 @@ Report current state of a Fleet agent.

```js
POST /api/fleet/agents/a4937110-e53e-11e9-934f-47a8e38a522c/checkin
Authorization: ApiKey VALID_ACCESS_API_KEY
{
"events": [{
"type": "STATE",
Expand Down
11 changes: 6 additions & 5 deletions x-pack/legacy/plugins/fleet/dev_docs/api/agents_enroll.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Enroll agent

## Headers

- `kbn-fleet-enrollment-token` (Required, string) A fleet enrollment token.
- `Authorization` (Required, string) a valid enrollemnt api key.

## Request body

Expand All @@ -20,12 +20,13 @@ Enroll agent

`200` Indicates a successful call.
`400` For an invalid request.
`401` For an invalid kbn-fleet-enrollment-token.
`401` For an invalid api key.

## Example

```js
POST /api/fleet/agents/enroll
Authorization: ApiKey VALID_API_KEY
{
"type": "PERMANENT",
"metadata": {
Expand All @@ -50,20 +51,20 @@ The API returns the following:
"user_provided_metadata": {},
"local_metadata": {},
"actions": [],
"access_token": "ACCESS_TOKEN"
"access_api_key": "ACCESS_API_KEY"
}
}
```

## Expected errors

The API will return a response with a `401` status code and an error if the enrollment token is invalid like this:
The API will return a response with a `401` status code and an error if the enrollment apiKey is invalid like this:

```js
{
"statusCode": 401,
"error": "Unauthorized",
"message": "Enrollment token is not valid: invalid token"
"message": "Enrollment apiKey is not valid: Enrollement api key does not exists or is not active"
}
```

Expand Down
14 changes: 7 additions & 7 deletions x-pack/legacy/plugins/fleet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const config = Joi.object({
export function fleet(kibana: any) {
return new kibana.Plugin({
id: PLUGIN.ID,
require: ['kibana', 'elasticsearch', 'xpack_main', 'encrypted_saved_objects', 'ingest'],
require: ['kibana', 'elasticsearch', 'xpack_main', 'encryptedSavedObjects', 'ingest'],
publicDir: resolve(__dirname, 'public'),
uiExports: {
// app: {
Expand All @@ -46,7 +46,7 @@ export function fleet(kibana: any) {
// TODO https://github.com/elastic/kibana/issues/46373
// indexPattern: INDEX_NAMES.EVENT,
},
tokens: {
enrollment_api_keys: {
isNamespaceAgnostic: true,
// TODO https://github.com/elastic/kibana/issues/46373
// indexPattern: INDEX_NAMES.FLEET,
Expand All @@ -57,9 +57,9 @@ export function fleet(kibana: any) {
config: () => config,
configPrefix: CONFIG_PREFIX,
init(server: any) {
server.plugins.encrypted_saved_objects.registerType({
type: 'tokens',
attributesToEncrypt: new Set(['token']),
server.newPlatform.setup.plugins.encryptedSavedObjects.registerType({
type: 'enrollment_api_keys',
attributesToEncrypt: new Set(['api_key']),
attributesToExcludeFromAAD: new Set(['enrollment_rules']),
});
server.plugins.xpack_main.registerFeature({
Expand All @@ -70,7 +70,7 @@ export function fleet(kibana: any) {
privileges: {
all: {
savedObject: {
all: ['agents', 'events', 'tokens'],
all: ['agents', 'events', 'enrollment_api_keys'],
read: [],
},
ui: ['read', 'write'],
Expand All @@ -79,7 +79,7 @@ export function fleet(kibana: any) {
read: {
savedObject: {
all: [],
read: ['agents', 'events', 'tokens'],
read: ['agents', 'events', 'enrollment_api_keys'],
},
ui: ['read'],
api: ['fleet-read'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,7 @@ function useAddMetadataForm(agent: Agent, done: () => void) {
function setError(error: AxiosError) {
setState({
isLoading: false,
error:
error.isAxiosError && error.response && error.response.data
? error.response.data.message
: error.message,
error: error.response && error.response.data ? error.response.data.message : error.message,
});
}

Expand Down
25 changes: 1 addition & 24 deletions x-pack/legacy/plugins/fleet/public/pages/agent_details/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,7 @@
import React, { SFC } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiHorizontalRule,
EuiPageBody,
EuiPageContent,
EuiCallOut,
EuiText,
EuiSpacer,
} from '@elastic/eui';
import { EuiPageBody, EuiPageContent, EuiCallOut, EuiText, EuiSpacer } from '@elastic/eui';
import { RouteComponentProps } from 'react-router-dom';
import { Loading } from '../../components/loading';
import { AgentEventsTable } from './components/agent_events_table';
Expand Down Expand Up @@ -78,20 +69,6 @@ export const AgentDetailsPage: SFC<Props> = ({
<AgentDetailSection agent={agent} />
<EuiSpacer size="xl" />
<AgentEventsTable agent={agent} />
<EuiFlexGroup gutterSize="xl">
<EuiFlexItem grow={3}>
<EuiFlexItem grow={null}>
<EuiHorizontalRule />
</EuiFlexItem>
<EuiFlexItem grow={null}>
<EuiHorizontalRule />
</EuiFlexItem>
<EuiFlexItem grow={null} />
</EuiFlexItem>
<EuiFlexItem grow={7}>
<EuiFlexItem grow={null} />
</EuiFlexItem>
</EuiFlexGroup>
</Layout>
</AgentRefreshContext.Provider>
);
Expand Down
5 changes: 2 additions & 3 deletions x-pack/legacy/plugins/fleet/public/pages/agent_list/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,8 @@ export const AgentListPage: React.SFC<{}> = () => {
setIsPoliciesLoading(false);
};

// Load initial list of agents
// Load initial list of policies
useEffect(() => {
fetchAgents();
fetchPolicies();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [showInactive]);
Expand All @@ -118,7 +117,7 @@ export const AgentListPage: React.SFC<{}> = () => {
fetchAgents();
setAreAllAgentsSelected(false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pagination, search, selectedPolicies]);
}, [pagination, search, showInactive, selectedPolicies]);

// Poll for agents on interval
useInterval(() => {
Expand Down
20 changes: 10 additions & 10 deletions x-pack/legacy/plugins/fleet/scripts/dev_agent/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const CHECKIN_INTERVAL = 3000; // 3 seconds

interface Agent {
id: string;
access_token: string;
access_api_key: string;
}

let closing = false;
Expand All @@ -27,11 +27,11 @@ run(
throw createFlagError('please provide a single --path flag');
}

if (!flags.enrollmentToken || typeof flags.enrollmentToken !== 'string') {
throw createFlagError('please provide a single --path flag');
if (!flags.enrollmentApiKey || typeof flags.enrollmentApiKey !== 'string') {
throw createFlagError('please provide a single --enrollmentApiKey flag');
}
const kibanaUrl: string = (flags.kibanaUrl as string) || 'http://localhost:5601';
const agent = await enroll(kibanaUrl, flags.enrollmentToken as string, log);
const agent = await enroll(kibanaUrl, flags.enrollmentApiKey as string, log);

log.info('Enrolled with sucess', agent);

Expand All @@ -46,10 +46,10 @@ run(
Run a fleet development agent.
`,
flags: {
string: ['kibanaUrl', 'enrollmentToken'],
string: ['kibanaUrl', 'enrollmentApiKey'],
help: `
--kibanaUrl kibanaURL to run the fleet agent
--enrollmentToken enrollment token
--enrollmentApiKey enrollment api key
`,
},
}
Expand All @@ -72,7 +72,7 @@ async function checkin(kibanaURL: string, agent: Agent, log: ToolingLog) {
}),
headers: {
'kbn-xsrf': 'xxx',
'kbn-fleet-access-token': agent.access_token,
Authorization: `ApiKey ${agent.access_api_key}`,
},
});

Expand All @@ -86,7 +86,7 @@ async function checkin(kibanaURL: string, agent: Agent, log: ToolingLog) {
log.info('checkin', json);
}

async function enroll(kibanaURL: string, token: string, log: ToolingLog): Promise<Agent> {
async function enroll(kibanaURL: string, apiKey: string, log: ToolingLog): Promise<Agent> {
const res = await fetch(`${kibanaURL}/api/fleet/agents/enroll`, {
method: 'POST',
body: JSON.stringify({
Expand All @@ -106,7 +106,7 @@ async function enroll(kibanaURL: string, token: string, log: ToolingLog): Promis
}),
headers: {
'kbn-xsrf': 'xxx',
'kbn-fleet-enrollment-token': token,
Authorization: `ApiKey ${apiKey}`,
},
});
const json = await res.json();
Expand All @@ -118,6 +118,6 @@ async function enroll(kibanaURL: string, token: string, log: ToolingLog): Promis

return {
id: json.item.id,
access_token: json.item.access_token,
access_api_key: json.item.access_api_key,
};
}
54 changes: 12 additions & 42 deletions x-pack/legacy/plugins/fleet/scripts/dev_env_setup/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
import { createFlagError, run } from '@kbn/dev-utils';
import fetch from 'node-fetch';

interface Policy {
id: string;
}

run(
async ({ flags, log }) => {
const kibanaUrl = flags.kibanaUrl || 'http://localhost:5601';
Expand All @@ -28,14 +24,12 @@ run(
throw createFlagError('please provide a single --kibanaPassword flag');
}

const policy = await createPolicy(kibanaUrl, kibanaUser, kibanaPassword);
log.info('Policy created', policy);
const token = await getEnrollmentToken(kibanaUrl, kibanaUser, kibanaPassword, policy.id);
log.info('Enrollment token', token);
const apiKey = await createEnrollmentApiKey(kibanaUrl, kibanaUser, kibanaPassword);
log.info('Enrollment API Key', apiKey);
},
{
description: `
Setup a fleet test policy and generate an enrollment token.
Setup a fleet enrollment API Key.
`,
flags: {
string: ['kibanaUrl', 'kibanaUser', 'kibanaPassword'],
Expand All @@ -48,48 +42,24 @@ run(
}
);

async function createPolicy(
async function createEnrollmentApiKey(
kibanaURL: string,
kibanaUser: string,
kibanaPassword: string
): Promise<Policy> {
const res = await fetch(`${kibanaURL}/api/ingest/policies`, {
kibanaPassword: string,
policyId?: string
): Promise<string> {
const res = await fetch(`${kibanaURL}/api/fleet/enrollment-api-keys`, {
method: 'POST',
body: JSON.stringify({
policy_id: policyId,
}),
headers: {
'kbn-xsrf': 'xsrf',
'content-type': 'application/json',
authorization: `Basic ${Buffer.from(`${kibanaUser}:${kibanaPassword}`).toString('base64')}`,
},
body: JSON.stringify({
name: 'Dev policy',
}),
});

const json = await res.json();

return {
id: json.item.id,
};
}

async function getEnrollmentToken(
kibanaURL: string,
kibanaUser: string,
kibanaPassword: string,
policyId: string
): Promise<string> {
const res = await fetch(
`${kibanaURL}/api/fleet/policies/${policyId}/enrollment-tokens?regenerate=true`,
{
method: 'GET',
headers: {
'kbn-xsrf': 'xsrf',
'content-type': 'application/json',
authorization: `Basic ${Buffer.from(`${kibanaUser}:${kibanaPassword}`).toString('base64')}`,
},
}
);

const json = await res.json();
return json.item.token;
return json.item.api_key;
}
4 changes: 2 additions & 2 deletions x-pack/legacy/plugins/fleet/scripts/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ You can run a development fleet agent that is going to enroll and checkin every
For this you can run the following command in the fleet pluging directory.

```
node scripts/dev_agent --enrollmentToken=<enrollmentToken> --kibanaUrl=http://localhost:5603/qed
node scripts/dev_agent --enrollmentApiKey=<enrollmentApiKey> --kibanaUrl=http://localhost:5603/qed
```

To generate a dummy config and an enrollment token you can use this script
To generate a dummy config and an enrollment enrollmentApiKey you can use this script

```
node scripts/dev_env_setup --kibanaUrl=http://localhost:5603/qed --kibanaUser=elastic --kibanaPassword=changeme
Expand Down
Loading

0 comments on commit 3612b98

Please sign in to comment.