Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fleet] Allow packages to specify index privileges #112397

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
56f4617
tidy: move default index privs to const
hop-dev Sep 13, 2021
902e2d0
add index privileges to package policy SO schema
hop-dev Sep 15, 2021
38cd557
add default index privileges const
hop-dev Sep 15, 2021
03a120a
add privileges to epm schema
hop-dev Sep 15, 2021
bd40551
add privileges to input stream types
hop-dev Sep 15, 2021
bfacfdf
use new const for default index privileges
hop-dev Sep 15, 2021
0e9ba6c
permissions being added to policy
hop-dev Sep 15, 2021
64ae520
fix unit tests
hop-dev Sep 15, 2021
3595370
add note about export
hop-dev Sep 16, 2021
2458206
tidy: move default index privs to const
hop-dev Sep 13, 2021
44848a0
add index privileges to package policy SO schema
hop-dev Sep 15, 2021
968f445
add default index privileges const
hop-dev Sep 15, 2021
608ff31
add privileges to epm schema
hop-dev Sep 15, 2021
df2c50d
add privileges to input stream types
hop-dev Sep 15, 2021
f7ebe3b
use new const for default index privileges
hop-dev Sep 15, 2021
eb22020
permissions being added to policy
hop-dev Sep 15, 2021
1fa59f9
fix unit tests
hop-dev Sep 15, 2021
fd61fa4
add note about export
hop-dev Sep 16, 2021
8a9db30
remove outdated tests
hop-dev Sep 16, 2021
0315d99
Merge branch 'feature-109047-override-index-privileges' of github.com…
hop-dev Sep 16, 2021
55d9c7d
Merge remote-tracking branch 'upstream/master' into feature-109047-ov…
hop-dev Sep 16, 2021
dd8ff25
return enabled check to start of function
hop-dev Sep 16, 2021
c5da9dd
Merge branch 'master' into feature-109047-override-index-privileges
kibanamachine Sep 20, 2021
fd71710
add privileges to SO mapping
hop-dev Sep 20, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions x-pack/plugins/fleet/common/constants/package_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
*/

export const PACKAGE_POLICY_SAVED_OBJECT_TYPE = 'ingest-package-policies';

export const PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES = ['auto_configure', 'create_doc'];
5 changes: 2 additions & 3 deletions x-pack/plugins/fleet/common/types/models/epm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@ export enum RegistryDataStreamKeys {
ingest_pipeline = 'ingest_pipeline',
elasticsearch = 'elasticsearch',
dataset_is_prefix = 'dataset_is_prefix',
permissions = 'permissions',
}

export interface RegistryDataStream {
Expand All @@ -297,15 +296,15 @@ export interface RegistryDataStream {
[RegistryDataStreamKeys.ingest_pipeline]?: string;
[RegistryDataStreamKeys.elasticsearch]?: RegistryElasticsearch;
[RegistryDataStreamKeys.dataset_is_prefix]?: boolean;
[RegistryDataStreamKeys.permissions]?: RegistryDataStreamPermissions;
}

export interface RegistryElasticsearch {
privileges?: RegistryDataStreamPrivileges;
'index_template.settings'?: estypes.IndicesIndexSettings;
'index_template.mappings'?: estypes.MappingTypeMapping;
}

export interface RegistryDataStreamPermissions {
export interface RegistryDataStreamPrivileges {
cluster?: string[];
indices?: string[];
}
Expand Down
5 changes: 5 additions & 0 deletions x-pack/plugins/fleet/common/types/models/package_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ export interface NewPackagePolicyInputStream {
data_stream: {
dataset: string;
type: string;
elasticsearch?: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering if we should update the saved object mapping here https://github.com/elastic/kibana/blob/master/x-pack/plugins/fleet/server/saved_objects/index.ts/#L251-L253 (as input is not enabled it probably change nothing)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @nchaulet I've added it now. Privileges may include cluster privileges in the future as well, I've set it as flattened as I dont think we would ever need to search on individual privileges?

privileges?: {
indices?: string[];
};
};
};
vars?: PackagePolicyConfigRecord;
config?: PackagePolicyConfigRecord;
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/server/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export {
DEFAULT_FLEET_SERVER_AGENT_POLICY,
DEFAULT_OUTPUT,
DEFAULT_PACKAGES,
PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES,
// Fleet Server index
FLEET_SERVER_SERVERS_INDEX,
ENROLLMENT_API_KEYS_INDEX,
Expand Down
5 changes: 5 additions & 0 deletions x-pack/plugins/fleet/server/saved_objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ const getSavedObjectTypes = (
properties: {
dataset: { type: 'keyword' },
type: { type: 'keyword' },
elasticsearch: {
properties: {
privileges: { type: 'flattened' },
},
},
},
},
vars: { type: 'flattened' },
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/fleet/server/services/agent_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
AGENT_POLICY_SAVED_OBJECT_TYPE,
AGENT_SAVED_OBJECT_TYPE,
PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE,
PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES,
} from '../constants';
import type {
PackagePolicy,
Expand Down Expand Up @@ -825,7 +826,7 @@ class AgentPolicyService {
permissions._elastic_agent_checks.indices = [
{
names,
privileges: ['auto_configure', 'create_doc'],
privileges: PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES,
},
];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { PackagePolicy, RegistryDataStream } from '../types';

import { getPackageInfo } from './epm/packages';
import {
getDataStreamPermissions,
getDataStreamPrivileges,
storedPackagePoliciesToAgentPermissions,
} from './package_policies_to_agent_permissions';

Expand Down Expand Up @@ -380,22 +380,22 @@ describe('storedPackagePoliciesToAgentPermissions()', () => {
});
});

describe('getDataStreamPermissions()', () => {
it('returns defaults for a datastream with no permissions', () => {
describe('getDataStreamPrivileges()', () => {
it('returns defaults for a datastream with no privileges', () => {
const dataStream = { type: 'logs', dataset: 'test' } as RegistryDataStream;
const permissions = getDataStreamPermissions(dataStream);
const privileges = getDataStreamPrivileges(dataStream);

expect(permissions).toMatchObject({
expect(privileges).toMatchObject({
names: ['logs-test-*'],
privileges: ['auto_configure', 'create_doc'],
});
});

it('adds the namespace to the index name', () => {
const dataStream = { type: 'logs', dataset: 'test' } as RegistryDataStream;
const permissions = getDataStreamPermissions(dataStream, 'namespace');
const privileges = getDataStreamPrivileges(dataStream, 'namespace');

expect(permissions).toMatchObject({
expect(privileges).toMatchObject({
names: ['logs-test-namespace'],
privileges: ['auto_configure', 'create_doc'],
});
Expand All @@ -407,9 +407,9 @@ describe('getDataStreamPermissions()', () => {
dataset: 'test',
dataset_is_prefix: true,
} as RegistryDataStream;
const permissions = getDataStreamPermissions(dataStream, 'namespace');
const privileges = getDataStreamPrivileges(dataStream, 'namespace');

expect(permissions).toMatchObject({
expect(privileges).toMatchObject({
names: ['logs-test.*-namespace'],
privileges: ['auto_configure', 'create_doc'],
});
Expand All @@ -421,25 +421,27 @@ describe('getDataStreamPermissions()', () => {
dataset: 'test',
hidden: true,
} as RegistryDataStream;
const permissions = getDataStreamPermissions(dataStream, 'namespace');
const privileges = getDataStreamPrivileges(dataStream, 'namespace');

expect(permissions).toMatchObject({
expect(privileges).toMatchObject({
names: ['.logs-test-namespace'],
privileges: ['auto_configure', 'create_doc'],
});
});

it('uses custom permissions if they are present in the datastream', () => {
it('uses custom privileges if they are present in the datastream', () => {
const dataStream = {
type: 'logs',
dataset: 'test',
permissions: { indices: ['read', 'write'] },
elasticsearch: {
privileges: { indices: ['read', 'monitor'] },
},
} as RegistryDataStream;
const permissions = getDataStreamPermissions(dataStream, 'namespace');
const privileges = getDataStreamPrivileges(dataStream, 'namespace');

expect(permissions).toMatchObject({
expect(privileges).toMatchObject({
names: ['logs-test-namespace'],
privileges: ['read', 'write'],
privileges: ['read', 'monitor'],
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
*/
import type { SavedObjectsClientContract } from 'kibana/server';

import type { FullAgentPolicyOutputPermissions, RegistryDataStreamPermissions } from '../../common';
import type { FullAgentPolicyOutputPermissions, RegistryDataStreamPrivileges } from '../../common';
import { PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES } from '../constants';
import { getPackageInfo } from '../../server/services/epm/packages';

import type { PackagePolicy } from '../types';

export const DEFAULT_PERMISSIONS = {
Expand All @@ -22,7 +22,7 @@ export const DEFAULT_PERMISSIONS = {
'synthetics-*',
'.logs-endpoint.diagnostic.collection-*',
],
privileges: ['auto_configure', 'create_doc'],
privileges: PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES,
},
],
};
Expand Down Expand Up @@ -104,12 +104,16 @@ export async function storedPackagePoliciesToAgentPermissions(
return;
}

const ds = {
const ds: DataStreamMeta = {
type: stream.data_stream.type,
dataset:
stream.compiled_stream?.data_stream?.dataset ?? stream.data_stream.dataset,
};

if (stream.data_stream.elasticsearch) {
ds.elasticsearch = stream.data_stream.elasticsearch;
}

dataStreams_.push(ds);
});

Expand All @@ -121,7 +125,7 @@ export async function storedPackagePoliciesToAgentPermissions(
packagePolicy.name,
{
indices: dataStreamsForPermissions.map((ds) =>
getDataStreamPermissions(ds, packagePolicy.namespace)
getDataStreamPrivileges(ds, packagePolicy.namespace)
),
},
];
Expand All @@ -136,10 +140,12 @@ interface DataStreamMeta {
dataset: string;
dataset_is_prefix?: boolean;
hidden?: boolean;
permissions?: RegistryDataStreamPermissions;
elasticsearch?: {
privileges?: RegistryDataStreamPrivileges;
};
}

export function getDataStreamPermissions(dataStream: DataStreamMeta, namespace: string = '*') {
export function getDataStreamPrivileges(dataStream: DataStreamMeta, namespace: string = '*') {
let index = `${dataStream.type}-${dataStream.dataset}`;

if (dataStream.dataset_is_prefix) {
Expand All @@ -152,8 +158,12 @@ export function getDataStreamPermissions(dataStream: DataStreamMeta, namespace:

index += `-${namespace}`;

const privileges = dataStream?.elasticsearch?.privileges?.indices?.length
? dataStream.elasticsearch.privileges.indices
: PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES;

return {
names: [index],
privileges: dataStream.permissions?.indices || ['auto_configure', 'create_doc'],
privileges,
};
}
130 changes: 126 additions & 4 deletions x-pack/plugins/fleet/server/services/package_policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,25 @@ import {
import type { SavedObjectsClient, SavedObjectsUpdateResponse } from 'src/core/server';
import type { KibanaRequest } from 'kibana/server';

import type { PackageInfo, PackagePolicySOAttributes, AgentPolicySOAttributes } from '../types';
import type {
PackageInfo,
PackagePolicySOAttributes,
AgentPolicySOAttributes,
PostPackagePolicyDeleteCallback,
RegistryDataStream,
PackagePolicyInputStream,
} from '../types';
import { createPackagePolicyMock } from '../../common/mocks';

import type { PutPackagePolicyUpdateCallback, PostPackagePolicyCreateCallback } from '..';

import { createAppContextStartContractMock, xpackMocks } from '../mocks';

import type { PostPackagePolicyDeleteCallback } from '../types';

import type { DeletePackagePoliciesResponse } from '../../common';

import { IngestManagerError } from '../errors';

import { packagePolicyService } from './package_policy';
import { packagePolicyService, _applyIndexPrivileges } from './package_policy';
import { appContextService } from './app_context';

async function mockedGetAssetsData(_a: any, _b: any, dataset: string) {
Expand Down Expand Up @@ -1069,3 +1075,119 @@ describe('Package policy service', () => {
});
});
});

describe('_applyIndexPrivileges()', () => {
function createPackageStream(indexPrivileges?: string[]): RegistryDataStream {
const stream: RegistryDataStream = {
type: '',
dataset: '',
title: '',
release: '',
package: '',
path: '',
};

if (indexPrivileges) {
stream.elasticsearch = {
privileges: {
indices: indexPrivileges,
},
};
}

return stream;
}

function createInputStream(
opts: Partial<PackagePolicyInputStream> = {}
): PackagePolicyInputStream {
return {
id: '',
enabled: true,
data_stream: {
dataset: '',
type: '',
},
...opts,
};
}

beforeAll(async () => {
appContextService.start(createAppContextStartContractMock());
});

it('should do nothing if packageStream has no privileges', () => {
const packageStream = createPackageStream();
const inputStream = createInputStream();

const streamOut = _applyIndexPrivileges(packageStream, inputStream);
expect(streamOut).toEqual(inputStream);
});

it('should not apply privileges if all privileges are forbidden', () => {
const forbiddenPrivileges = ['write', 'delete', 'delete_index', 'all'];
const packageStream = createPackageStream(forbiddenPrivileges);
const inputStream = createInputStream();

const streamOut = _applyIndexPrivileges(packageStream, inputStream);
expect(streamOut).toEqual(inputStream);
});

it('should not apply privileges if all privileges are unrecognized', () => {
const unrecognizedPrivileges = ['idnotexist', 'invalidperm'];
const packageStream = createPackageStream(unrecognizedPrivileges);
const inputStream = createInputStream();

const streamOut = _applyIndexPrivileges(packageStream, inputStream);
expect(streamOut).toEqual(inputStream);
});

it('should apply privileges if all privileges are valid', () => {
const validPrivileges = [
'auto_configure',
'create_doc',
'maintenance',
'monitor',
'read',
'read_cross_cluster',
];

const packageStream = createPackageStream(validPrivileges);
const inputStream = createInputStream();
const expectedStream = {
...inputStream,
data_stream: {
...inputStream.data_stream,
elasticsearch: {
privileges: {
indices: validPrivileges,
},
},
},
};

const streamOut = _applyIndexPrivileges(packageStream, inputStream);
expect(streamOut).toEqual(expectedStream);
});

it('should only apply valid privileges when there is a mix of valid and invalid', () => {
const mixedPrivileges = ['auto_configure', 'read_cross_cluster', 'idontexist', 'delete'];

const packageStream = createPackageStream(mixedPrivileges);
const inputStream = createInputStream();
const expectedStream = {
...inputStream,
data_stream: {
...inputStream.data_stream,
elasticsearch: {
privileges: {
indices: ['auto_configure', 'read_cross_cluster'],
},
},
},
};

const streamOut = _applyIndexPrivileges(packageStream, inputStream);
expect(streamOut).toEqual(expectedStream);
});
});
Loading