[Security Solution] [Detections] Adds scripts to create users + roles based on specific privileges (#81866)

* shell scripts for creating roles + users for testing

* update readme's and updated privilege requirements based on testing with the users and inferring what the roles are supposed to do

* update role privileges based on feedback meeting yesterday

* updated scripts to accept filepath to role / user, added a test to ensure upload value list button is disabled

* updated role scripts to be parameterized

* adds login with role function and adds a sample test with a role to test that a t1 analyst user cannot upload a value list

* add object with corresponding roles

* fix spacing

* parameterize urls for basic auth with roles + users

* forgot to change the cy.visit string

* add KIBANA_URL env var for cli runner

* add env vars for curl script execution

* second script

* update readme's for each role and remove create_index from lists privilege for the soc manager role

* remove 'manage' cluster privilege for rule author

* remove 'create_index' privilege from soc_manager role since that is not parity with the security workflows spreadsheet

* update the login function logic with glo's feedback

* replace SIEM with Security Solution in markdown files

* make role param optional not just undefined

* remove unused file

* add copyright to scripts files

* update top-level README for roles scripts

* remove reference to internal spreadsheet and reference readme for this pr

* remove unnecessary -XPOST and remove verbose mode from post_detections_user script

* adds utils for running integration tests with other users and adds two sample tests showing example usage

* minor type updates and small refactor

* fix x-pack/test types

* use enum types instead of custom type

* fix path to json

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Xavier Mouligneau <189600+XavierM@users.noreply.github.com>
This commit is contained in:
Devin W. Hurley 2020-11-19 16:02:03 -05:00 committed by GitHub
parent 45d3861219
commit b3c334a1d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 1013 additions and 13 deletions

View file

@ -3,6 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { AlertAction } from '../../../alerts/common';
export type RuleAlertAction = Omit<AlertAction, 'actionTypeId'> & {

View file

@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
// For the source of these roles please consult the PR these were introduced https://github.com/elastic/kibana/pull/81866#issue-511165754
export enum ROLES {
t1_analyst = 't1_analyst',
t2_analyst = 't2_analyst',
hunter = 'hunter',
rule_author = 'rule_author',
soc_manager = 'soc_manager',
platform_engineer = 'platform_engineer',
detections_admin = 'detections_admin',
}
export type RolesType = keyof typeof ROLES;

View file

@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { loginAndWaitForPageWithoutDateRange } from '../tasks/login';
import { ROLES } from '../../common/test';
import { deleteRoleAndUser, loginAndWaitForPageWithoutDateRange } from '../tasks/login';
import { DETECTIONS_URL } from '../urls/navigation';
import {
waitForAlertsPanelToBeLoaded,
@ -24,7 +25,7 @@ import {
deleteValueListsFile,
exportValueList,
} from '../tasks/lists';
import { VALUE_LISTS_TABLE, VALUE_LISTS_ROW } from '../screens/lists';
import { VALUE_LISTS_TABLE, VALUE_LISTS_ROW, VALUE_LISTS_MODAL_ACTIVATOR } from '../screens/lists';
describe('value lists', () => {
describe('management modal', () => {
@ -220,4 +221,19 @@ describe('value lists', () => {
});
});
});
describe('user with restricted access role', () => {
beforeEach(() => {
loginAndWaitForPageWithoutDateRange(DETECTIONS_URL, ROLES.t1_analyst);
goToManageAlertsDetectionRules();
});
afterEach(() => {
deleteRoleAndUser(ROLES.t1_analyst);
});
it('Does not allow a t1 analyst user to upload a value list', () => {
cy.get(VALUE_LISTS_MODAL_ACTIVATOR).should('have.attr', 'disabled');
});
});
});

View file

@ -5,6 +5,9 @@
*/
import * as yaml from 'js-yaml';
import Url, { UrlObject } from 'url';
import { RolesType } from '../../common/test';
import { TIMELINE_FLYOUT_BODY } from '../screens/timeline';
/**
@ -42,6 +45,89 @@ const ELASTICSEARCH_PASSWORD = 'ELASTICSEARCH_PASSWORD';
*/
const LOGIN_API_ENDPOINT = '/internal/security/login';
/**
* cy.visit will default to the baseUrl which uses the default kibana test user
* This function will override that functionality in cy.visit by building the baseUrl
* directly from the environment variables set up in x-pack/test/security_solution_cypress/runner.ts
*
* @param role string role/user to log in with
* @param route string route to visit
*/
export const getUrlWithRoute = (role: RolesType, route: string) => {
const theUrl = `${Url.format({
auth: `${role}:changeme`,
username: role,
password: 'changeme',
protocol: Cypress.env('protocol'),
hostname: Cypress.env('hostname'),
port: Cypress.env('configport'),
} as UrlObject)}${route.startsWith('/') ? '' : '/'}${route}`;
cy.log(`origin: ${theUrl}`);
return theUrl;
};
export const getCurlScriptEnvVars = () => ({
ELASTICSEARCH_URL: Cypress.env('ELASTICSEARCH_URL'),
ELASTICSEARCH_USERNAME: Cypress.env('ELASTICSEARCH_USERNAME'),
ELASTICSEARCH_PASSWORD: Cypress.env('ELASTICSEARCH_PASSWORD'),
KIBANA_URL: Cypress.env('KIBANA_URL'),
});
export const postRoleAndUser = (role: RolesType) => {
const env = getCurlScriptEnvVars();
const detectionsRoleScriptPath = `./server/lib/detection_engine/scripts/roles_users/${role}/post_detections_role.sh`;
const detectionsRoleJsonPath = `./server/lib/detection_engine/scripts/roles_users/${role}/detections_role.json`;
const detectionsUserScriptPath = `./server/lib/detection_engine/scripts/roles_users/${role}/post_detections_user.sh`;
const detectionsUserJsonPath = `./server/lib/detection_engine/scripts/roles_users/${role}/detections_user.json`;
// post the role
cy.exec(`bash ${detectionsRoleScriptPath} ${detectionsRoleJsonPath}`, {
env,
});
// post the user associated with the role to elasticsearch
cy.exec(`bash ${detectionsUserScriptPath} ${detectionsUserJsonPath}`, {
env,
});
};
export const deleteRoleAndUser = (role: RolesType) => {
const env = getCurlScriptEnvVars();
const detectionsUserDeleteScriptPath = `./server/lib/detection_engine/scripts/roles_users/${role}/delete_detections_user.sh`;
// delete the role
cy.exec(`bash ${detectionsUserDeleteScriptPath}`, {
env,
});
};
export const loginWithRole = async (role: RolesType) => {
postRoleAndUser(role);
const theUrl = Url.format({
auth: `${role}:changeme`,
username: role,
password: 'changeme',
protocol: Cypress.env('protocol'),
hostname: Cypress.env('hostname'),
port: Cypress.env('configport'),
} as UrlObject);
cy.log(`origin: ${theUrl}`);
cy.request({
body: {
providerType: 'basic',
providerName: 'basic',
currentURL: '/',
params: {
username: role,
password: 'changeme',
},
},
headers: { 'kbn-xsrf': 'cypress-creds-via-config' },
method: 'POST',
url: getUrlWithRoute(role, LOGIN_API_ENDPOINT),
});
};
/**
* Authenticates with Kibana using, if specified, credentials specified by
* environment variables. The credentials in `kibana.dev.yml` will be used
@ -50,8 +136,10 @@ const LOGIN_API_ENDPOINT = '/internal/security/login';
* To speed the execution of tests, prefer this non-interactive authentication,
* which is faster than authentication via Kibana's interactive login page.
*/
export const login = () => {
if (credentialsProvidedByEnvironment()) {
export const login = (role?: RolesType) => {
if (role != null) {
loginWithRole(role);
} else if (credentialsProvidedByEnvironment()) {
loginViaEnvironmentCredentials();
} else {
loginViaConfig();
@ -129,8 +217,8 @@ const loginViaConfig = () => {
* Authenticates with Kibana, visits the specified `url`, and waits for the
* Kibana global nav to be displayed before continuing
*/
export const loginAndWaitForPage = (url: string) => {
login();
export const loginAndWaitForPage = (url: string, role?: RolesType) => {
login(role);
cy.viewport('macbook-15');
cy.visit(
`${url}?timerange=(global:(linkTo:!(timeline),timerange:(from:1547914976217,fromStr:'2019-01-19T16:22:56.217Z',kind:relative,to:1579537385745,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1547914976217,fromStr:'2019-01-19T16:22:56.217Z',kind:relative,to:1579537385745,toStr:now)))`
@ -138,17 +226,19 @@ export const loginAndWaitForPage = (url: string) => {
cy.get('[data-test-subj="headerGlobalNav"]');
};
export const loginAndWaitForPageWithoutDateRange = (url: string) => {
login();
export const loginAndWaitForPageWithoutDateRange = (url: string, role?: RolesType) => {
login(role);
cy.viewport('macbook-15');
cy.visit(url);
cy.visit(role ? getUrlWithRoute(role, url) : url);
cy.get('[data-test-subj="headerGlobalNav"]', { timeout: 120000 });
};
export const loginAndWaitForTimeline = (timelineId: string) => {
login();
export const loginAndWaitForTimeline = (timelineId: string, role?: RolesType) => {
const route = `/app/security/timelines?timeline=(id:'${timelineId}',isOpen:!t)`;
login(role);
cy.viewport('macbook-15');
cy.visit(`/app/security/timelines?timeline=(id:'${timelineId}',isOpen:!t)`);
cy.visit(role ? getUrlWithRoute(role, route) : route);
cy.get('[data-test-subj="headerGlobalNav"]');
cy.get(TIMELINE_FLYOUT_BODY).should('be.visible');
};

View file

@ -10,7 +10,7 @@
"build-graphql-types": "node scripts/generate_types_from_graphql.js",
"cypress:open": "../../../node_modules/.bin/cypress open --config-file ./cypress/cypress.json",
"cypress:open-as-ci": "node ../../../scripts/functional_tests --config ../../test/security_solution_cypress/visual_config.ts",
"cypress:run": "../../../node_modules/.bin/cypress run --browser chrome --headless --spec ./cypress/integration/**/*.spec.ts --config-file ./cypress/cypress.json --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json; status=$?; ../../node_modules/.bin/mochawesome-merge ../../../target/kibana-security-solution/cypress/results/mochawesome*.json > ../../../target/kibana-security-solution/cypress/results/output.json; ../../../node_modules/.bin/marge ../../../target/kibana-security-solution/cypress/results/output.json --reportDir ../../../target/kibana-security-solution/cypress/results; mkdir -p ../../../target/junit && cp ../../../target/kibana-security-solution/cypress/results/*.xml ../../../target/junit/ && exit $status;",
"cypress:run": "../../../node_modules/.bin/cypress run --browser chrome --headless --spec ./cypress/integration/**/*.spec.ts --config-file ./cypress/cypress.json --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./cypress/reporter_config.json; status=$?; ../../../node_modules/.bin/mochawesome-merge ../../../target/kibana-security-solution/cypress/results/mochawesome*.json > ../../../target/kibana-security-solution/cypress/results/output.json; ../../../node_modules/.bin/marge ../../../target/kibana-security-solution/cypress/results/output.json --reportDir ../../../target/kibana-security-solution/cypress/results; mkdir -p ../../../target/junit && cp ../../../target/kibana-security-solution/cypress/results/*.xml ../../../target/junit/ && exit $status;",
"cypress:run-as-ci": "node --max-old-space-size=2048 ../../../scripts/functional_tests --config ../../test/security_solution_cypress/cli_config.ts",
"test:generate": "node scripts/endpoint/resolver_generator"
}

View file

@ -0,0 +1,12 @@
1. When first starting up elastic, detections will not be available until you visit the page with a SOC Manager role or Platform Engineer role
2. I gave the Hunter role "all" privileges for saved objects management and builtInAlerts so that they can create rules.
3. Rule Author has the ability to create rules and create value lists
| Role | Data Sources | Security Solution ML Jobs/Results | Lists | Rules/Exceptions | Action Connectors | Signals/Alerts |
| :------------------------------------------: | :----------: | :-------------------------------: | :---------: | :--------------: | :---------------: | :------------------------------: |
| T1 Analyst | read | read | none | read | read | read, write |
| T2 Analyst | read | read | read | read | read | read, write |
| Hunter / T3 Analyst | read, write | read | read | read, write | read | read, write |
| Rule Author / Manager / Detections Engineer | read, write | read | read, write | read, write | read | read, write, view_index_metadata |
| SOC Manager | read, write | read | read, write | read, write | all | read, write, manage |
| Platform Engineer (data ingest, cluster ops) | read, write | all | all | read, write | all | all |

View file

@ -0,0 +1 @@
This user contains all the possible privileges listed in our detections privileges docs https://www.elastic.co/guide/en/security/current/detections-permissions-section.html This user has higher privileges than the Platform Engineer user

View file

@ -0,0 +1,10 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -v -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XDELETE ${ELASTICSEARCH_URL}/_security/user/detections_admin

View file

@ -0,0 +1,35 @@
{
"elasticsearch": {
"cluster": ["manage"],
"indices": [
{
"names": [
".siem-signals-*",
".lists*",
".items*",
"apm-*-transaction*",
"auditbeat-*",
"endgame-*",
"filebeat-*",
"logs-*",
"packetbeat-*",
"winlogbeat-*"
],
"privileges": ["manage", "write", "read"]
}
]
},
"kibana": [
{
"feature": {
"ml": ["all"],
"siem": ["all"],
"actions": ["read"],
"builtInAlerts": ["all"],
"dev_tools": ["all"],
"savedObjectsManagement": ["all"]
},
"spaces": ["*"]
}
]
}

View file

@ -0,0 +1,6 @@
{
"password": "changeme",
"roles": ["detections_admin"],
"full_name": "Detections User",
"email": "detections-user@example.com"
}

View file

@ -0,0 +1,10 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XGET ${KIBANA_URL}/api/security/role/detections_admin | jq -S .

View file

@ -0,0 +1,11 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XPUT ${KIBANA_URL}/api/security/role/detections_admin \
-d @detections_role.json

View file

@ -0,0 +1,13 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
USER=(${@:-./detections_user.json})
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
${ELASTICSEARCH_URL}/_security/user/detections_admin \
-d @${USER}

View file

@ -0,0 +1,12 @@
This user can CRUD rules and signals. The main difference here is the user has
```json
"builtInAlerts": ["all"],
"savedObjectsManagement": ["all"]
```
privileges whereas the T1 and T2 have "read" privileges which prevents them from creating rules
| Role | Data Sources | Security Solution ML Jobs/Results | Lists | Rules/Exceptions | Action Connectors | Signals/Alerts |
| :-----------------: | :----------: | :------------------: | :---: | :--------------: | :---------------: | :------------: |
| Hunter / T3 Analyst | read, write | read | read | read, write | read | read, write |

View file

@ -0,0 +1,10 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -v -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XDELETE ${ELASTICSEARCH_URL}/_security/user/hunter

View file

@ -0,0 +1,39 @@
{
"elasticsearch": {
"cluster": [],
"indices": [
{
"names": [
"apm-*-transaction*",
"auditbeat-*",
"endgame-*",
"filebeat-*",
"logs-*",
"packetbeat-*",
"winlogbeat-*"
],
"privileges": ["read", "write"]
},
{
"names": [".siem-signals-*"],
"privileges": ["read", "write"]
},
{
"names": [".lists*", ".items*"],
"privileges": ["read", "write"]
}
]
},
"kibana": [
{
"feature": {
"ml": ["read"],
"siem": ["all"],
"actions": ["read"],
"builtInAlerts": ["all"],
"savedObjectsManagement": ["all"]
},
"spaces": ["*"]
}
]
}

View file

@ -0,0 +1,6 @@
{
"password": "changeme",
"roles": ["hunter"],
"full_name": "Hunter",
"email": "detections-reader@example.com"
}

View file

@ -0,0 +1,10 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XGET ${KIBANA_URL}/api/security/role/hunter | jq -S .

View file

@ -0,0 +1,13 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
ROLE=(${@:-./detections_role.json})
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XPUT ${KIBANA_URL}/api/security/role/hunter \
-d @${ROLE}

View file

@ -0,0 +1,13 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
USER=(${@:-./detections_user.json})
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
${ELASTICSEARCH_URL}/_security/user/hunter \
-d @${USER}

View file

@ -0,0 +1,5 @@
essentially a superuser for security solution
| Role | Data Sources | Security Solution ML Jobs/Results | Lists | Rules/Exceptions | Action Connectors | Signals/Alerts |
| :------------------------------------------: | :----------: | :------------------: | :---: | :--------------: | :---------------: | :------------: |
| Platform Engineer (data ingest, cluster ops) | all | all | all | read, write | all | all |

View file

@ -0,0 +1,10 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -v -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XDELETE ${ELASTICSEARCH_URL}/_security/user/platform_engineer

View file

@ -0,0 +1,39 @@
{
"elasticsearch": {
"cluster": ["manage"],
"indices": [
{
"names": [".lists*", ".items*"],
"privileges": ["all"]
},
{
"names": [
"apm-*-transaction*",
"auditbeat-*",
"endgame-*",
"filebeat-*",
"logs-*",
"packetbeat-*",
"winlogbeat-*"
],
"privileges": ["all"]
},
{
"names": [".siem-signals-*"],
"privileges": ["all"]
}
]
},
"kibana": [
{
"feature": {
"ml": ["all"],
"siem": ["all"],
"actions": ["all"],
"builtInAlerts": ["all"],
"savedObjectsManagement": ["all"]
},
"spaces": ["*"]
}
]
}

View file

@ -0,0 +1,6 @@
{
"password": "changeme",
"roles": ["platform_engineer"],
"full_name": "platform engineer",
"email": "detections-reader@example.com"
}

View file

@ -0,0 +1,10 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XGET ${KIBANA_URL}/api/security/role/platform_engineer | jq -S .

View file

@ -0,0 +1,13 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
ROLE=(${@:-./detections_role.json})
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XPUT ${KIBANA_URL}/api/security/role/platform_engineer \
-d @${ROLE}

View file

@ -0,0 +1,14 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
USER=(${@:-./detections_user.json})
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
${ELASTICSEARCH_URL}/_security/user/platform_engineer \
-d @${USER}

View file

@ -0,0 +1,5 @@
rule author has the same privileges as hunter with the additional privileges of uploading value lists
| Role | Data Sources | Security Solution ML Jobs/Results | Lists | Rules/Exceptions | Action Connectors | Signals/Alerts |
| :-----------------------------------------: | :----------: | :------------------: | :---------: | :--------------: | :---------------: | :------------------------------: |
| Rule Author / Manager / Detections Engineer | read, write | read | read, write | read, write | read | read, write, view_index_metadata |

View file

@ -0,0 +1,10 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -v -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XDELETE ${ELASTICSEARCH_URL}/_security/user/rule_author

View file

@ -0,0 +1,37 @@
{
"elasticsearch": {
"cluster": [],
"indices": [
{
"names": [
"apm-*-transaction*",
"auditbeat-*",
"endgame-*",
"filebeat-*",
"logs-*",
"packetbeat-*",
"winlogbeat-*",
".lists*",
".items*"
],
"privileges": ["read", "write"]
},
{
"names": [".siem-signals-*"],
"privileges": ["read", "write", "view_index_metadata"]
}
]
},
"kibana": [
{
"feature": {
"ml": ["read"],
"siem": ["all"],
"actions": ["read"],
"builtInAlerts": ["all"],
"savedObjectsManagement": ["all"]
},
"spaces": ["*"]
}
]
}

View file

@ -0,0 +1,6 @@
{
"password": "changeme",
"roles": ["rule_author"],
"full_name": "rule author",
"email": "detections-reader@example.com"
}

View file

@ -0,0 +1,10 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XGET ${KIBANA_URL}/api/security/role/rule_author | jq -S .

View file

@ -0,0 +1,13 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
ROLE=(${@:-./detections_role.json})
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XPUT ${KIBANA_URL}/api/security/role/rule_author \
-d @${ROLE}

View file

@ -0,0 +1,13 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
USER=(${@:-./detections_user.json})
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
${ELASTICSEARCH_URL}/_security/user/rule_author \
-d @${USER}

View file

@ -0,0 +1,5 @@
SOC Manager has all of the privileges of a rule author role with the additional privilege of managing the signals index. It can't create the signals index though.
| Role | Data Sources | Security Solution ML Jobs/Results | Lists | Rules/Exceptions | Action Connectors | Signals/Alerts |
| :---------: | :----------: | :------------------: | :---------: | :--------------: | :---------------: | :-----------------: |
| SOC Manager | read, write | read | read, write | read, write | all | read, write, manage |

View file

@ -0,0 +1,10 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -v -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XDELETE ${ELASTICSEARCH_URL}/_security/user/soc_manager

View file

@ -0,0 +1,37 @@
{
"elasticsearch": {
"cluster": [],
"indices": [
{
"names": [
"apm-*-transaction*",
"auditbeat-*",
"endgame-*",
"filebeat-*",
"logs-*",
"packetbeat-*",
"winlogbeat-*",
".lists*",
".items*"
],
"privileges": ["read", "write"]
},
{
"names": [".siem-signals-*"],
"privileges": ["read", "write", "manage"]
}
]
},
"kibana": [
{
"feature": {
"ml": ["read"],
"siem": ["all"],
"actions": ["all"],
"builtInAlerts": ["all"],
"savedObjectsManagement": ["all"]
},
"spaces": ["*"]
}
]
}

View file

@ -0,0 +1,6 @@
{
"password": "changeme",
"roles": ["soc_manager"],
"full_name": "SOC manager",
"email": "detections-reader@example.com"
}

View file

@ -0,0 +1,10 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XGET ${KIBANA_URL}/api/security/role/soc_manager | jq -S .

View file

@ -0,0 +1,14 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
ROLE=(${@:-./detections_role.json})
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XPUT ${KIBANA_URL}/api/security/role/soc_manager \
-d @${ROLE}

View file

@ -0,0 +1,14 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
USER=(${@:-./detections_user.json})
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
${ELASTICSEARCH_URL}/_security/user/soc_manager \
-d @${USER}

View file

@ -0,0 +1,3 @@
| Role | Data Sources | Security Solution ML Jobs/Results | Lists | Rules/Exceptions | Actions Connectors | Signals/Alerts |
| :--------: | :----------: | :------------------: | :---: | :--------------: | :----------------: | :------------: |
| T1 Analyst | read | read | none | read | read | read, write |

View file

@ -0,0 +1,10 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -v -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XDELETE ${ELASTICSEARCH_URL}/_security/user/t1_analyst

View file

@ -0,0 +1,32 @@
{
"elasticsearch": {
"cluster": [],
"indices": [
{ "names": [".siem-signals-*"], "privileges": ["read", "write"] },
{
"names": [
"apm-*-transaction*",
"auditbeat-*",
"endgame-*",
"filebeat-*",
"logs-*",
"packetbeat-*",
"winlogbeat-*"
],
"privileges": ["read"]
}
]
},
"kibana": [
{
"feature": {
"ml": ["read"],
"siem": ["all"],
"actions": ["read"],
"builtInAlerts": ["read"],
"savedObjectsManagement": ["read"]
},
"spaces": ["*"]
}
]
}

View file

@ -0,0 +1,6 @@
{
"password": "changeme",
"roles": ["t1_analyst"],
"full_name": "T1 Analyst",
"email": "detections-reader@example.com"
}

View file

@ -0,0 +1,10 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XGET ${KIBANA_URL}/api/security/role/t1_analyst | jq -S .

View file

@ -0,0 +1,14 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
# Uses a default if no argument is specified
ROLE=(${@:-./detections_role.json})
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XPUT ${KIBANA_URL}/api/security/role/t1_analyst \
-d @${ROLE}

View file

@ -0,0 +1,13 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
USER=(${@:-./detections_user.json})
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
${ELASTICSEARCH_URL}/_security/user/t1_analyst \
-d @${USER}

View file

@ -0,0 +1,5 @@
This role can view rules. Essentially there is no difference between a T1 and T2 analyst.
| Role | Data Sources | Security Solution ML Jobs/Results | Lists | Rules/Exceptions | Action Connectors | Signals/Alerts |
| :--------: | :----------: | :------------------: | :---: | :--------------: | :---------------: | :------------: |
| T2 Analyst | read | read | read | read | read | read, write |

View file

@ -0,0 +1,10 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -v -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XDELETE ${ELASTICSEARCH_URL}/_security/user/t2_analyst

View file

@ -0,0 +1,34 @@
{
"elasticsearch": {
"cluster": [],
"indices": [
{ "names": [".siem-signals-*"], "privileges": ["read", "write"] },
{
"names": [
".lists*",
".items*",
"apm-*-transaction*",
"auditbeat-*",
"endgame-*",
"filebeat-*",
"logs-*",
"packetbeat-*",
"winlogbeat-*"
],
"privileges": ["read"]
}
]
},
"kibana": [
{
"feature": {
"ml": ["read"],
"siem": ["all"],
"actions": ["read"],
"builtInAlerts": ["read"],
"savedObjectsManagement": ["read"]
},
"spaces": ["*"]
}
]
}

View file

@ -0,0 +1,6 @@
{
"password": "changeme",
"roles": ["t2_analyst"],
"full_name": "t2 analyst",
"email": "detections-reader@example.com"
}

View file

@ -0,0 +1,10 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XGET ${KIBANA_URL}/api/security/role/t2_analyst | jq -S .

View file

@ -0,0 +1,13 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
ROLE=(${@:-./detections_role.json})
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
-XPUT ${KIBANA_URL}/api/security/role/t2_analyst \
-d @${ROLE}

View file

@ -0,0 +1,13 @@
#
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.
#
USER=(${@:-./detections_user.json})
curl -H 'Content-Type: application/json' -H 'kbn-xsrf: 123'\
-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
${ELASTICSEARCH_URL}/_security/user/t2_analyst \
-d @${USER}

View file

@ -0,0 +1,109 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import * as t1AnalystUser from '../../../../plugins/security_solution/server/lib/detection_engine/scripts/roles_users/t1_analyst/detections_user.json';
import * as t2AnalystUser from '../../../../plugins/security_solution/server/lib/detection_engine/scripts/roles_users/t2_analyst/detections_user.json';
import * as hunterUser from '../../../../plugins/security_solution/server/lib/detection_engine/scripts/roles_users/hunter/detections_user.json';
import * as ruleAuthorUser from '../../../../plugins/security_solution/server/lib/detection_engine/scripts/roles_users/rule_author/detections_user.json';
import * as socManagerUser from '../../../../plugins/security_solution/server/lib/detection_engine/scripts/roles_users/soc_manager/detections_user.json';
import * as platformEngineerUser from '../../../../plugins/security_solution/server/lib/detection_engine/scripts/roles_users/platform_engineer/detections_user.json';
import * as detectionsAdminUser from '../../../../plugins/security_solution/server/lib/detection_engine/scripts/roles_users/detections_admin/detections_user.json';
import * as t1AnalystRole from '../../../../plugins/security_solution/server/lib/detection_engine/scripts/roles_users/t1_analyst/detections_role.json';
import * as t2AnalystRole from '../../../../plugins/security_solution/server/lib/detection_engine/scripts/roles_users/t2_analyst/detections_role.json';
import * as hunterRole from '../../../../plugins/security_solution/server/lib/detection_engine/scripts/roles_users/hunter/detections_role.json';
import * as ruleAuthorRole from '../../../../plugins/security_solution/server/lib/detection_engine/scripts/roles_users/rule_author/detections_role.json';
import * as socManagerRole from '../../../../plugins/security_solution/server/lib/detection_engine/scripts/roles_users/soc_manager/detections_role.json';
import * as platformEngineerRole from '../../../../plugins/security_solution/server/lib/detection_engine/scripts/roles_users/platform_engineer/detections_role.json';
import * as detectionsAdminRole from '../../../../plugins/security_solution/server/lib/detection_engine/scripts/roles_users/detections_admin/detections_role.json';
import { ROLES } from '../../../../plugins/security_solution/common/test';
import { FtrProviderContext } from '../../common/ftr_provider_context';
export const createUserAndRole = async (
securityService: ReturnType<FtrProviderContext['getService']>,
role: keyof typeof ROLES
) => {
switch (role) {
case ROLES.detections_admin:
await postRoleAndUser(
ROLES.detections_admin,
detectionsAdminRole,
detectionsAdminUser,
securityService
);
break;
case ROLES.t1_analyst:
await postRoleAndUser(ROLES.t1_analyst, t1AnalystRole, t1AnalystUser, securityService);
break;
case ROLES.t2_analyst:
await postRoleAndUser(ROLES.t2_analyst, t2AnalystRole, t2AnalystUser, securityService);
break;
case ROLES.hunter:
await postRoleAndUser(ROLES.hunter, hunterRole, hunterUser, securityService);
break;
case ROLES.rule_author:
await postRoleAndUser(ROLES.rule_author, ruleAuthorRole, ruleAuthorUser, securityService);
break;
case ROLES.soc_manager:
await postRoleAndUser(ROLES.soc_manager, socManagerRole, socManagerUser, securityService);
break;
case ROLES.platform_engineer:
await postRoleAndUser(
ROLES.platform_engineer,
platformEngineerRole,
platformEngineerUser,
securityService
);
break;
default:
break;
}
};
interface UserInterface {
password: string;
roles: string[];
full_name: string;
email: string;
}
interface RoleInterface {
elasticsearch: {
cluster: string[];
indices: Array<{
names: string[];
privileges: string[];
}>;
};
kibana: Array<{
feature: {
ml: string[];
siem: string[];
actions: string[];
builtInAlerts: string[];
savedObjectsManagement: string[];
};
spaces: string[];
}>;
}
export const postRoleAndUser = async (
roleName: string,
role: RoleInterface,
user: UserInterface,
securityService: ReturnType<FtrProviderContext['getService']>
) => {
await securityService.role.create(roleName, {
kibana: role.kibana,
elasticsearch: role.elasticsearch,
});
await securityService.user.create(roleName, {
password: 'changeme',
full_name: user.full_name,
roles: user.roles,
});
};

View file

@ -25,12 +25,16 @@ import {
waitForSignalsToBePresent,
getAllSignals,
} from '../../utils';
import { createUserAndRole } from '../roles_users_utils';
import { ROLES } from '../../../../plugins/security_solution/common/test';
// eslint-disable-next-line import/no-default-export
export default ({ getService }: FtrProviderContext) => {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const es = getService('es');
const supertestWithoutAuth = getService('supertestWithoutAuth');
const securityService = getService('security');
describe('open_close_signals', () => {
describe('validation checks', () => {
@ -157,6 +161,79 @@ export default ({ getService }: FtrProviderContext) => {
);
expect(everySignalClosed).to.eql(true);
});
it('should NOT be able to close signals with t1 analyst user', async () => {
const rule = { ...getSimpleRule(), from: '1900-01-01T00:00:00.000Z', query: '*:*' };
await createRule(supertest, rule);
await waitForSignalsToBePresent(supertest);
await createUserAndRole(securityService, ROLES.t1_analyst);
const signalsOpen = await getAllSignals(supertest);
const signalIds = signalsOpen.hits.hits.map((signal) => signal._id);
// Try to set all of the signals to the state of closed.
// This should not be possible with the given user.
await supertestWithoutAuth
.post(DETECTION_ENGINE_SIGNALS_STATUS_URL)
.set('kbn-xsrf', 'true')
.auth(ROLES.t1_analyst, 'changeme')
.send(setSignalStatus({ signalIds, status: 'closed' }))
.expect(403);
// query for the signals with the superuser
// to allow a check that the signals were NOT closed with t1 analyst
const {
body: signalsClosed,
}: { body: SearchResponse<{ signal: Signal }> } = await supertest
.post(DETECTION_ENGINE_QUERY_SIGNALS_URL)
.set('kbn-xsrf', 'true')
.send(getQuerySignalIds(signalIds))
.expect(200);
const everySignalOpen = signalsClosed.hits.hits.every(
({
_source: {
signal: { status },
},
}) => status === 'open'
);
expect(everySignalOpen).to.eql(true);
});
it('should be able to close signals with soc_manager user', async () => {
const rule = { ...getSimpleRule(), from: '1900-01-01T00:00:00.000Z', query: '*:*' };
await createRule(supertest, rule);
await waitForSignalsToBePresent(supertest);
const userAndRole = ROLES.soc_manager;
await createUserAndRole(securityService, userAndRole);
const signalsOpen = await getAllSignals(supertest);
const signalIds = signalsOpen.hits.hits.map((signal) => signal._id);
// Try to set all of the signals to the state of closed.
// This should not be possible with the given user.
await supertestWithoutAuth
.post(DETECTION_ENGINE_SIGNALS_STATUS_URL)
.set('kbn-xsrf', 'true')
.auth(userAndRole, 'changeme') // each user has the same password
.send(setSignalStatus({ signalIds, status: 'closed' }))
.expect(200);
const {
body: signalsClosed,
}: { body: SearchResponse<{ signal: Signal }> } = await supertest
.post(DETECTION_ENGINE_QUERY_SIGNALS_URL)
.set('kbn-xsrf', 'true')
.send(getQuerySignalIds(signalIds))
.expect(200);
const everySignalClosed = signalsClosed.hits.hits.every(
({
_source: {
signal: { status },
},
}) => status === 'closed'
);
expect(everySignalClosed).to.eql(true);
});
});
});
});

View file

@ -28,9 +28,20 @@ export async function SecuritySolutionCypressCliTestRunner({ getService }: FtrPr
FORCE_COLOR: '1',
// eslint-disable-next-line @typescript-eslint/naming-convention
CYPRESS_baseUrl: Url.format(config.get('servers.kibana')),
// eslint-disable-next-line @typescript-eslint/naming-convention
CYPRESS_protocol: config.get('servers.kibana.protocol'),
// eslint-disable-next-line @typescript-eslint/naming-convention
CYPRESS_hostname: config.get('servers.kibana.hostname'),
// eslint-disable-next-line @typescript-eslint/naming-convention
CYPRESS_configport: config.get('servers.kibana.port'),
CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')),
CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'),
CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'),
CYPRESS_KIBANA_URL: Url.format({
protocol: config.get('servers.kibana.protocol'),
hostname: config.get('servers.kibana.hostname'),
port: config.get('servers.kibana.port'),
}),
...process.env,
},
wait: true,
@ -55,9 +66,20 @@ export async function SecuritySolutionCypressVisualTestRunner({ getService }: Ft
FORCE_COLOR: '1',
// eslint-disable-next-line @typescript-eslint/naming-convention
CYPRESS_baseUrl: Url.format(config.get('servers.kibana')),
// eslint-disable-next-line @typescript-eslint/naming-convention
CYPRESS_protocol: config.get('servers.kibana.protocol'),
// eslint-disable-next-line @typescript-eslint/naming-convention
CYPRESS_hostname: config.get('servers.kibana.hostname'),
// eslint-disable-next-line @typescript-eslint/naming-convention
CYPRESS_configport: config.get('servers.kibana.port'),
CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')),
CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'),
CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'),
CYPRESS_KIBANA_URL: Url.format({
protocol: config.get('servers.kibana.protocol'),
hostname: config.get('servers.kibana.hostname'),
port: config.get('servers.kibana.port'),
}),
...process.env,
},
wait: true,