Skip to content

Commit

Permalink
adds an 'owner' field to the siem-signals mapping, working authz get …
Browse files Browse the repository at this point in the history
…for security solution, need to work through rule registry changes (#10)

* adds an 'owner' field to the siem-signals mapping, working authz get for security solution, need to work through rule registry changes

* minor cleanup

* undo owner change in rule registry, will come in different pr

* enhances user experience of test scripts

* response error
  • Loading branch information
dhurley14 authored Apr 30, 2021
1 parent 241f3a0 commit cd62a2f
Show file tree
Hide file tree
Showing 13 changed files with 149 additions and 33 deletions.
7 changes: 4 additions & 3 deletions x-pack/plugins/monitoring/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,14 @@ export class MonitoringPlugin
router.get({ path: '/monitoring-myfakepath', validate: false }, async (context, req, res) => {
try {
const racClient = await context.ruleRegistry?.getRacClient();
const thing = await racClient?.get({ id: 'hello world', owner: 'observability' });
const thing = await racClient?.find({ owner: 'observability' });
console.error('THE THING!!!', JSON.stringify(thing.body, null, 2));
return res.ok({ body: { success: true } });
return res.ok({ body: { success: true, alerts: thing.body.hits.hits } });
} catch (err) {
console.error('monitoring route threw an error');
console.error(err);
return res.notFound({ body: { message: err.message } });
return res.unauthorized({ body: { message: err.message } });
// return res.customError({ statusCode: err.statusCode, body: { message: err.message } });
}
});
}
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/rule_registry/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { schema, TypeOf } from '@kbn/config-schema';
import { PluginInitializerContext } from 'src/core/server';
import { RuleRegistryPlugin } from './plugin';

export { RacPluginSetupContract } from './plugin';
export { RuleRegistryPluginSetupContract } from './plugin';
export { createLifecycleRuleTypeFactory } from './rule_registry/rule_type_helpers/create_lifecycle_rule_type_factory';
export { FieldMapOf } from './types';
export { ScopedRuleRegistryClient } from './rule_registry/create_scoped_rule_registry_client/types';
Expand Down
92 changes: 81 additions & 11 deletions x-pack/plugins/rule_registry/server/rac_client/rac_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
GrantAPIKeyResult as SecurityPluginGrantAPIKeyResult,
InvalidateAPIKeyResult as SecurityPluginInvalidateAPIKeyResult,
} from '../../../security/server';
import { SERVER_APP_ID } from '../../../security_solution/server';

import {
RacAuthorization,
WriteOperations,
Expand Down Expand Up @@ -61,6 +63,11 @@ export interface FindOptions extends IndexType {
filter?: string;
}

export interface CreateAlertParams {
esClient: ElasticsearchClient;
owner: 'observability' | 'securitySolution';
}

interface IndexType {
[key: string]: unknown;
}
Expand Down Expand Up @@ -117,14 +124,8 @@ export class RacClient {
owner,
}: {
id: string;
owner: 'securitySolution' | 'observability';
owner: typeof SERVER_APP_ID | 'observability';
}): Promise<unknown> {
// TODO: type alert for the get method
const result = await this.esClient.search({
index: '.siem*',
body: { query: { match_all: {} } },
});
console.error(`************\nRESULT ${JSON.stringify(result, null, 2)}\n************`);
// .get<RawAlert>('alert', id);
try {
await this.authorization.ensureAuthorized(
Expand All @@ -133,7 +134,25 @@ export class RacClient {
owner,
ReadOperations.Get
);
// TODO: type alert for the get method

try {
const result = await this.esClient.get({
index: '.siem-signals-devin-hurley-default',
id,
});
console.error(`************\nRESULT ${JSON.stringify(result, null, 2)}\n************`);
return result;
} catch (exc) {
console.error('THREW ERROR WHEN TRYING GET', JSON.stringify(exc, null, 2));
}

// const result = await this.esClient.search({
// index: '.siem*',
// body: { query: { match_all: {} } },
// });
} catch (error) {
console.error('HERES THE ERROR', error);
// this.auditLogger?.log(
// alertAuditEvent({
// action: AlertAuditAction.GET,
Expand All @@ -152,7 +171,7 @@ export class RacClient {
// TODO: strip out owner field maybe?
// this.getAlertFromRaw<Params>(result.id, result.attributes, result.references);

return result;
// return result;

// return Promise.resolve({ id: 'hello world!!!' });
// const result = await this.unsecuredSavedObjectsClient.get<RawAlert>('alert', id);
Expand Down Expand Up @@ -181,9 +200,58 @@ export class RacClient {
// return this.getAlertFromRaw<Params>(result.id, result.attributes, result.references);
}

public async find<Params extends AlertTypeParams = never>({
options: { fields, ...options } = {},
}: { options?: FindOptions } = {}): Promise<FindResult<Params>> {
public async find({
owner,
}: {
owner: typeof SERVER_APP_ID | 'observability';
}): Promise<unknown> {
try {
await this.authorization.ensureAuthorized(
// TODO: add spaceid here.. I think
// result.body._source?.owner,
owner,
ReadOperations.Get
);
// TODO: type alert for the get method

try {
// const result = await this.esClient.get({
// index: '.siem-signals-devin-hurley-default',
// id: 'ecf1d03a9f3456bb28bf3af5ef9fd2ef441641f3b495d92112e5e76d8feae62e',
// });
const result = await this.esClient.search({
index: '.siem-signals*',
body: {
query: {
term: {
'signal.owner': {
value: owner,
},
},
},
},
});
console.error(`************\nRESULT ${JSON.stringify(result, null, 2)}\n************`);
return result;
} catch (exc) {
console.error('THREW ERROR WHEN TRYING GET', JSON.stringify(exc, null, 2));
}

// const result = await this.esClient.search({
// index: '.siem*',
// body: { query: { match_all: {} } },
// });
} catch (error) {
console.error('HERES THE ERROR', error);
// this.auditLogger?.log(
// alertAuditEvent({
// action: AlertAuditAction.GET,
// savedObject: { type: 'alert', id },
// error,
// })
// );
throw error;
}
// let authorizationTuple;
// try {
// authorizationTuple = await this.authorization.getFindAuthorizationFilter();
Expand Down Expand Up @@ -337,4 +405,6 @@ export class RacClient {
// ]);
// return updateResult;
}

static async create({ esClient, owner, data }: createAlertParams) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export function createLifecycleRuleTypeFactory(): CreateLifecycleRuleType<BaseRu
'@timestamp': timestamp,
'event.kind': 'state',
'kibana.rac.alert.id': alertId,
// 'owner': ''
};

const isNew = !state.trackedAlerts[alertId];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ set -e

USER=${1:-'observer'}

cd ./hunter && sh ./post_detections_role.sh && sh ./post_detections_user.sh
cd ../observer && sh ./post_detections_role.sh && sh ./post_detections_user.sh
cd ..

# Example: ./find_rules.sh
curl -s -k \
-u $USER:changeme \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@

set -e

cd ./hunter && sh ./post_detections_role.sh && sh ./post_detections_user.sh
cd ../observer && sh ./post_detections_role.sh && sh ./post_detections_user.sh
cd ..


USER=${1:-'hunter'}

# Example: ./find_rules.sh
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/security_solution/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { PluginInitializerContext, PluginConfigDescriptor } from '../../../../src/core/server';
import { Plugin, PluginSetup, PluginStart } from './plugin';
import { configSchema, ConfigType } from './config';
import { SIGNALS_INDEX_KEY } from '../common/constants';
import { SIGNALS_INDEX_KEY, SERVER_APP_ID } from '../common/constants';
import { AppClient } from './types';

export const plugin = (context: PluginInitializerContext) => {
Expand Down Expand Up @@ -46,7 +46,7 @@ export const config: PluginConfigDescriptor<ConfigType> = {
};

export { ConfigType, Plugin, PluginSetup, PluginStart };
export { AppClient };
export { AppClient, SERVER_APP_ID };

// Exports to be shared with plugins such as x-pack/lists plugin
export { deleteTemplate } from './lib/detection_engine/index/delete_template';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@
}
}
},
"owner": {
"type": "keyword"
},
"rule": {
"properties": {
"id": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@ import {
import { buildRuleWithoutOverrides, buildRuleWithOverrides } from './build_rule';
import { additionalSignalFields, buildSignal } from './build_signal';
import { buildEventTypeSignal } from './build_event_type_signal';
import { SERVER_APP_ID } from '../../../../common/constants';
import { EqlSequence } from '../../../../common/detection_engine/types';
import { generateSignalId, wrapBuildingBlocks, wrapSignal } from './utils';

// format search_after result for signals index.
export const buildBulkBody = (
ruleSO: SavedObject<AlertAttributes>,
doc: SignalSourceHit
doc: SignalSourceHit,
owner: typeof SERVER_APP_ID
): SignalHit => {
const rule = buildRuleWithOverrides(ruleSO, doc._source!);
const signal: Signal = {
...buildSignal([doc], rule),
...buildSignal([doc], rule, owner),
...additionalSignalFields(doc),
};
const event = buildEventTypeSignal(doc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { SearchTypes } from '../../../../common/detection_engine/types';
import { SERVER_APP_ID } from '../../../../common/constants';
import { RulesSchema } from '../../../../common/detection_engine/schemas/response/rules_schema';
import { SIGNALS_TEMPLATE_VERSION } from '../routes/index/get_signals_template';
import { isEventTypeSignal } from './build_event_type_signal';
Expand Down Expand Up @@ -76,7 +77,11 @@ export const removeClashes = (doc: BaseSignalHit): BaseSignalHit => {
* @param docs The parent signals/events of the new signal to be built.
* @param rule The rule that is generating the new signal.
*/
export const buildSignal = (docs: BaseSignalHit[], rule: RulesSchema): Signal => {
export const buildSignal = (
docs: BaseSignalHit[],
rule: RulesSchema,
owner: typeof SERVER_APP_ID
): Signal => {
const _meta = {
version: SIGNALS_TEMPLATE_VERSION,
};
Expand All @@ -92,6 +97,7 @@ export const buildSignal = (docs: BaseSignalHit[], rule: RulesSchema): Signal =>
parents,
ancestors,
status: 'open',
owner,
rule,
depth,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
AlertInstanceState,
AlertServices,
} from '../../../../../alerting/server';

import { SERVER_APP_ID } from '../../../../common/constants';
import { AlertAttributes, SignalHit, SignalSearchResponse, WrappedSignalHit } from './types';
import { RefreshTypes } from '../types';
import { generateId, makeFloatString, errorAggregator } from './utils';
Expand Down Expand Up @@ -121,10 +123,10 @@ export const singleBulkCreate = async ({
),
},
},
buildBulkBody(ruleSO, doc),
buildBulkBody(ruleSO, doc, SERVER_APP_ID),
]);
const start = performance.now();
const { body: response } = await services.scopedClusterClient.asCurrentUser.bulk({
const { body: response } = await services.scopedClusterClient.asInternalUser.bulk({
index: signalsIndex,
refresh,
body: bulkBody,
Expand All @@ -140,7 +142,7 @@ export const singleBulkCreate = async ({
.map((doc, index) => ({
_id: response.items[index].create?._id ?? '',
_index: response.items[index].create?._index ?? '',
...buildBulkBody(ruleSO, doc),
...buildBulkBody(ruleSO, doc, SERVER_APP_ID),
}))
.filter((_, index) => get(response.items[index], 'create.status') === 201);
const createdItemsCount = createdItems.length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ export interface Signal {
};
original_time?: string;
original_event?: SearchTypes;
owner?: string;
status: Status;
threshold_result?: ThresholdResult;
original_signal?: SearchTypes;
Expand Down
41 changes: 31 additions & 10 deletions x-pack/plugins/security_solution/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,13 +217,34 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
router.get({ path: '/security-myfakepath', validate: false }, async (context, req, res) => {
try {
const racClient = await context.ruleRegistry?.getRacClient();
const thing = await racClient?.get({ id: 'hello world', owner: 'securitySolution' });
console.error('THE THING EXISTS??', JSON.stringify(thing.body, null, 2));
return res.ok({ body: { success: true } });
const thing = await racClient?.find({ owner: SERVER_APP_ID });
console.error('hits?', JSON.stringify(thing.body.hits.hits, null, 2));
return res.ok({ body: { success: true, alerts: thing.body.hits.hits } });
} catch (err) {
console.error('monitoring route threw an error');
console.error(err);
return res.notFound({ body: { message: err.message } });
console.error('ERROR JSON', JSON.stringify(err, null, 2));
const statusCode = err.output.statusCode;
console.error('ERROR STATUSCODE?', statusCode);
// { message: err.message },

// const contentType = {
// 'Content-Type': 'application/json',
// };
// const defaultedHeaders = {
// ...contentType,
// };

// return res.custom({
// statusCode,
// headers: defaultedHeaders,
// body: Buffer.from(
// JSON.stringify({
// message: 'hello world', //err.message,
// status_code: statusCode,
// })
// ),
// });
return res.unauthorized({ body: { message: err.message } });
}
});

Expand All @@ -236,12 +257,12 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
order: 1100,
app: [...securitySubPlugins, 'kibana'],
category: DEFAULT_APP_CATEGORIES.security,
rac: ['securitySolution'],
rac: [SERVER_APP_ID],
privileges: {
all: {
app: [...securitySubPlugins, 'kibana'],
rac: {
all: ['securitySolution'],
all: [SERVER_APP_ID],
},
savedObject: {
all: [
Expand Down Expand Up @@ -275,11 +296,11 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
insightsAndAlerting: ['triggersActions'],
},
alerting: ruleTypes,
rac: ['securitySolution'],
rac: [SERVER_APP_ID],
privileges: {
all: {
rac: {
all: ['securitySolution'],
all: [SERVER_APP_ID],
},
app: [...securitySubPlugins, 'kibana'],
catalogue: ['securitySolution'],
Expand Down Expand Up @@ -320,7 +341,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
read: ruleTypes,
},
rac: {
all: ['securitySolution'],
all: [SERVER_APP_ID],
},
management: {
insightsAndAlerting: ['triggersActions'],
Expand Down

0 comments on commit cd62a2f

Please sign in to comment.