Skip to content

Commit

Permalink
[7.x] [Security Solution] [Detections] Adds scripts to create users +…
Browse files Browse the repository at this point in the history
… roles based on specific privileges (#81866) (#83861)

* 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>

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Xavier Mouligneau <189600+XavierM@users.noreply.github.com>
  • Loading branch information
3 people authored Nov 19, 2020
1 parent b88833d commit 9390d4e
Show file tree
Hide file tree
Showing 58 changed files with 1,013 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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'> & {
Expand Down
18 changes: 18 additions & 0 deletions x-pack/plugins/security_solution/common/test/index.ts
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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', () => {
Expand Down Expand Up @@ -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');
});
});
});
110 changes: 100 additions & 10 deletions x-pack/plugins/security_solution/cypress/tasks/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand Down Expand Up @@ -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
Expand All @@ -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();
Expand Down Expand Up @@ -129,26 +217,28 @@ 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)))`
);
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');
};
2 changes: 1 addition & 1 deletion x-pack/plugins/security_solution/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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 |
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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": ["*"]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"password": "changeme",
"roles": ["detections_admin"],
"full_name": "Detections User",
"email": "detections-user@example.com"
}
Original file line number Diff line number Diff line change
@@ -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 .
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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}
Original file line number Diff line number Diff line change
@@ -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 |
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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": ["*"]
}
]
}
Loading

0 comments on commit 9390d4e

Please sign in to comment.