From 10126bb7ef05aeed6545dde9a9151faee9b9677e Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Thu, 2 Sep 2021 09:44:57 -0400 Subject: [PATCH 1/8] Auth enhancements --- .../tools/sbin/so-elasticsearch-roles-load | 57 ++++++++ salt/common/tools/sbin/so-user | 132 ++++++++++++++---- salt/elasticsearch/init.sls | 29 +++- salt/elasticsearch/roles/analyst.json | 45 ++++++ salt/soc/files/kratos/schema.json | 4 - salt/soc/files/soc/custom_roles | 20 +++ salt/soc/files/soc/soc.json | 8 ++ salt/soc/init.sls | 11 ++ setup/so-functions | 2 +- 9 files changed, 269 insertions(+), 39 deletions(-) create mode 100644 salt/common/tools/sbin/so-elasticsearch-roles-load create mode 100644 salt/elasticsearch/roles/analyst.json create mode 100644 salt/soc/files/soc/custom_roles diff --git a/salt/common/tools/sbin/so-elasticsearch-roles-load b/salt/common/tools/sbin/so-elasticsearch-roles-load new file mode 100644 index 000000000..3b0f580fe --- /dev/null +++ b/salt/common/tools/sbin/so-elasticsearch-roles-load @@ -0,0 +1,57 @@ +#!/bin/bash +# Copyright 2014,2015,2016,2017,2018,2019,2020,2021 Security Onion Solutions, LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +{%- set mainint = salt['pillar.get']('host:mainint') %} +{%- set MYIP = salt['grains.get']('ip_interfaces:' ~ mainint)[0] %} + +default_conf_dir=/opt/so/conf +ELASTICSEARCH_HOST="{{ MYIP }}" +ELASTICSEARCH_PORT=9200 + +# Define a default directory to load roles from +ELASTICSEARCH_ROLES="$default_conf_dir/elasticsearch/roles/" + +# Wait for ElasticSearch to initialize +echo -n "Waiting for ElasticSearch..." +COUNT=0 +ELASTICSEARCH_CONNECTED="no" +while [[ "$COUNT" -le 240 ]]; do + {{ ELASTICCURL }} -k --output /dev/null --silent --head --fail -L https://"$ELASTICSEARCH_HOST":"$ELASTICSEARCH_PORT" + if [ $? -eq 0 ]; then + ELASTICSEARCH_CONNECTED="yes" + echo "connected!" + break + else + ((COUNT+=1)) + sleep 1 + echo -n "." + fi +done +if [ "$ELASTICSEARCH_CONNECTED" == "no" ]; then + echo + echo -e "Connection attempt timed out. Unable to connect to ElasticSearch. \nPlease try: \n -checking log(s) in /var/log/elasticsearch/\n -running 'sudo docker ps' \n -running 'sudo so-elastic-restart'" + echo +fi + +cd ${ELASTICSEARCH_ROLES} + +echo "Loading templates..." +for role in *; do + name=$(echo "$role" | cut -d. -f1) + so-elasticsearch-query security/roles/$name -XPUT -d @"$role" +done + +cd - >/dev/null diff --git a/salt/common/tools/sbin/so-user b/salt/common/tools/sbin/so-user index 742c3ca5d..7ec094efb 100755 --- a/salt/common/tools/sbin/so-user +++ b/salt/common/tools/sbin/so-user @@ -18,11 +18,17 @@ source $(dirname $0)/so-common +DEFAULT_ROLE=analyst + if [[ $# -lt 1 || $# -gt 2 ]]; then - echo "Usage: $0 [email]" + echo "Usage: $0 [email] [role]" + echo "" + echo " where is one of the following:" echo "" echo " list: Lists all user email addresses currently defined in the identity system" echo " add: Adds a new user to the identity system; requires 'email' parameter" + echo " addrole: Grants a role to an existing user; requires 'email' and 'role' parameters" + echo " delrole: Removes a role from an existing user; requires 'email' and 'role' parameters" echo " update: Updates a user's password; requires 'email' parameter" echo " enable: Enables a user; requires 'email' parameter" echo " disable: Disables a user; requires 'email' parameter" @@ -36,6 +42,7 @@ fi operation=$1 email=$2 +role=$3 kratosUrl=${KRATOS_URL:-http://127.0.0.1:4434} databasePath=${KRATOS_DB_PATH:-/opt/so/conf/kratos/db/db.sqlite} @@ -138,10 +145,9 @@ function updatePassword() { function createElasticFile() { filename=$1 - tmpFile=${filename} - truncate -s 0 "$tmpFile" - chmod 600 "$tmpFile" - chown "${esUID}:${esGID}" "$tmpFile" + truncate -s 0 "$filename" + chmod 600 "$filename" + chown "${esUID}:${esGID}" "$filename" } function syncElasticSystemUser() { @@ -174,28 +180,15 @@ function syncElasticSystemRole() { function syncElastic() { echo "Syncing users between SOC and Elastic..." usersTmpFile="${elasticUsersFile}.tmp" - rolesTmpFile="${elasticRolesFile}.tmp" createElasticFile "${usersTmpFile}" - createElasticFile "${rolesTmpFile}" authPillarJson=$(lookup_salt_value "auth" "elasticsearch" "pillar" "json") syncElasticSystemUser "$authPillarJson" "so_elastic_user" "$usersTmpFile" - syncElasticSystemRole "$authPillarJson" "so_elastic_user" "superuser" "$rolesTmpFile" - syncElasticSystemUser "$authPillarJson" "so_kibana_user" "$usersTmpFile" - syncElasticSystemRole "$authPillarJson" "so_kibana_user" "superuser" "$rolesTmpFile" - syncElasticSystemUser "$authPillarJson" "so_logstash_user" "$usersTmpFile" - syncElasticSystemRole "$authPillarJson" "so_logstash_user" "superuser" "$rolesTmpFile" - syncElasticSystemUser "$authPillarJson" "so_beats_user" "$usersTmpFile" - syncElasticSystemRole "$authPillarJson" "so_beats_user" "superuser" "$rolesTmpFile" - syncElasticSystemUser "$authPillarJson" "so_monitor_user" "$usersTmpFile" - syncElasticSystemRole "$authPillarJson" "so_monitor_user" "remote_monitoring_collector" "$rolesTmpFile" - syncElasticSystemRole "$authPillarJson" "so_monitor_user" "remote_monitoring_agent" "$rolesTmpFile" - syncElasticSystemRole "$authPillarJson" "so_monitor_user" "monitoring_user" "$rolesTmpFile" if [[ -f "$databasePath" ]]; then # Generate the new users file @@ -207,23 +200,12 @@ function syncElastic() { jq -r '.user + ":" + .data.hashed_password' \ >> "$usersTmpFile" [[ $? != 0 ]] && fail "Unable to read credential hashes from database" - - # Generate the new users_roles file - - echo "select 'superuser:' || ici.identifier " \ - "from identity_credential_identifiers ici, identity_credentials ic " \ - "where ici.identity_credential_id=ic.id and instr(ic.config, 'hashed_password') " \ - "order by ici.identifier;" | \ - sqlite3 "$databasePath" \ - >> "$rolesTmpFile" - [[ $? != 0 ]] && fail "Unable to read credential IDs from database" else echo "Database file does not exist yet, skipping users export" fi if [[ -s "${usersTmpFile}" ]]; then mv "${usersTmpFile}" "${elasticUsersFile}" - mv "${rolesTmpFile}" "${elasticRolesFile}" if [[ -z "$SKIP_STATE_APPLY" ]]; then echo "Elastic state will be re-applied to affected minions. This may take several minutes..." @@ -252,11 +234,73 @@ function listUsers() { response=$(curl -Ss -L ${kratosUrl}/identities) [[ $? != 0 ]] && fail "Unable to communicate with Kratos" - echo "${response}" | jq -r ".[] | .verifiable_addresses[0].value" | sort + users=$(echo "${response}" | jq -r ".[] | .verifiable_addresses[0].value" | sort) + for user in $users; do + roles=$(grep "$user" users_roles | cut -d: -f1 | tr '\n' ' ') + echo "$user: $roles" + done +} + +function addUserRole() { + email=$1 + role=$2 + + return adjustUserRole "$email" "$role" "add" +} + +function deleteUserRole() { + email=$1 + role=$2 + + return adjustUserRole "$email" "$role" "del" +} + +function adjustUserRole() { + email=$1 + role=$2 + op=$3 + + identityId=$(findIdByEmail "$email") + [[ ${identityId} == "" ]] && fail "User not found" + + if [ ! -f "$filename" ]; then + rolesTmpFile="${elasticRolesFile}.tmp" + createElasticFile "${rolesTmpFile}" + authPillarJson=$(lookup_salt_value "auth" "elasticsearch" "pillar" "json") + syncElasticSystemRole "$authPillarJson" "so_elastic_user" "superuser" "$rolesTmpFile" + syncElasticSystemRole "$authPillarJson" "so_kibana_user" "superuser" "$rolesTmpFile" + syncElasticSystemRole "$authPillarJson" "so_logstash_user" "superuser" "$rolesTmpFile" + syncElasticSystemRole "$authPillarJson" "so_beats_user" "superuser" "$rolesTmpFile" + syncElasticSystemRole "$authPillarJson" "so_monitor_user" "remote_monitoring_collector" "$rolesTmpFile" + syncElasticSystemRole "$authPillarJson" "so_monitor_user" "remote_monitoring_agent" "$rolesTmpFile" + syncElasticSystemRole "$authPillarJson" "so_monitor_user" "monitoring_user" "$rolesTmpFile" + mv "${rolesTmpFile}" "${elasticRolesFile}" + fi + + filename="$elasticRolesFile" + grep "$role:" "$elasticRolesFile" | grep "$email" && hasRole=1 + if [[ "$op" == "add" ]]; then + if [[ "$hasRole" -eq 1 ]]; then + fail "User '$email' already has the role: $role" + else + echo "$role:$email" >> "$filename" + fi + elif [[ "$op" == "del" ]]; then + if [[ "$hasRole" -ne 1 ]]; then + fail "User '$email' does not have the role: $role" + else + sed -i "/^$role:$email\$/d" "$filename" + fi + else + echo "Unsupported role adjustment operation: $op" + exit 1 + fi + return 0 } function createUser() { email=$1 + role=$1 now=$(date -u +%FT%TZ) addUserJson=$(cat < /opt/so/conf/elasticsearch/users && chown 930:930 /opt/so/conf/elasticsearch/users && chmod 600 /opt/so/conf/elasticsearch/users + - name: cat /opt/so/conf/elasticsearch/users.tmp > /opt/so/conf/elasticsearch/users && chown 930:939 /opt/so/conf/elasticsearch/users && chmod 660 /opt/so/conf/elasticsearch/users - onchanges: - file: /opt/so/conf/elasticsearch/users.tmp @@ -201,7 +218,7 @@ auth_users_roles_inode: require: - file: auth_users_roles cmd.run: - - name: cat /opt/so/conf/elasticsearch/users_roles.tmp > /opt/so/conf/elasticsearch/users_roles && chown 930:930 /opt/so/conf/elasticsearch/users_roles && chmod 600 /opt/so/conf/elasticsearch/users_roles + - name: cat /opt/so/conf/elasticsearch/users_roles.tmp > /opt/so/conf/elasticsearch/users_roles && chown 930:939 /opt/so/conf/elasticsearch/users_roles && chmod 660 /opt/so/conf/elasticsearch/users_roles - onchanges: - file: /opt/so/conf/elasticsearch/users_roles.tmp @@ -283,7 +300,7 @@ so-elasticsearch-pipelines: - file: esyml - file: so-elasticsearch-pipelines-file -{% if grains['role'] in ['so-manager', 'so-eval', 'so-managersearch', 'so-standalone', 'so-heavynode', 'so-node', 'so-import'] and TEMPLATES %} +{% if TEMPLATES %} so-elasticsearch-templates: cmd.run: - name: /usr/sbin/so-elasticsearch-templates-load @@ -291,6 +308,12 @@ so-elasticsearch-templates: - template: jinja {% endif %} +so-elasticsearch-roles-load: + cmd.run: + - name: /usr/sbin/so-elasticsearch-roles-load + - cwd: /opt/so + - template: jinja + {% endif %} {# if grains['role'] != 'so-helix' #} {% else %} diff --git a/salt/elasticsearch/roles/analyst.json b/salt/elasticsearch/roles/analyst.json new file mode 100644 index 000000000..f81c5a67e --- /dev/null +++ b/salt/elasticsearch/roles/analyst.json @@ -0,0 +1,45 @@ +{ + "elasticsearch": { + "cluster": [ + "cancel_task", + "create_snapshot", + "monitor", + "monitor_data_frame_transforms", + "monitor_ml", + "monitor_rollup", + "monitor_snapshot", + "monitor_text_structure", + "monitor_transform", + "monitor_watcher", + "read_ccr", + "read_ilm", + "read_pipeline", + "read_slm" + ], + "indices": [ + { + "names": [ + "so-*" + ], + "privileges": [ + "read", + "read_cross_cluster", + "monitor", + "view_index_metadata" + ] + } + ], + "run_as": [] + }, + "kibana": [ + { + "spaces": [ + "*" + ], + "base": [ + "read" + ], + "feature": {} + } + ] +} \ No newline at end of file diff --git a/salt/soc/files/kratos/schema.json b/salt/soc/files/kratos/schema.json index 986086936..19ee2197c 100644 --- a/salt/soc/files/kratos/schema.json +++ b/salt/soc/files/kratos/schema.json @@ -31,10 +31,6 @@ "type": "string", "title": "Last Name" }, - "role": { - "type": "string", - "title": "Role" - }, "status": { "type": "string", "title": "Status" diff --git a/salt/soc/files/soc/custom_roles b/salt/soc/files/soc/custom_roles new file mode 100644 index 000000000..80ae7b147 --- /dev/null +++ b/salt/soc/files/soc/custom_roles @@ -0,0 +1,20 @@ +# Define custom business role mappings, or remove mappings that come with +# the default SOC deployment. +# +# IMPORTANT: This file should be copied from the salt/default tree into +# the salt/local tree (preserving the same directory structure). +# Failure to do this will result in the customizations being +# overwritten on future upgrades. +# +# Syntax => prebuiltRoleX: customRoleY: op +# Explanation => roleY and roleZ are adjusted permissions of roleX, op is: +# + add the new permissions/role mappings (default) +# - remove existing prebuilt permissions +# +# In the example below, we will define a new role for junior analysts, +# that is nearly identical to the analyst role that comes with SOC, with the +# exception that it removes their ability to obtain details about other +# analysts in the system. +# +# analyst: jr_analyst +# user-monitor: jr_analyst:- diff --git a/salt/soc/files/soc/soc.json b/salt/soc/files/soc/soc.json index fc6d5f28d..6119f0e6b 100644 --- a/salt/soc/files/soc/soc.json +++ b/salt/soc/files/soc/soc.json @@ -85,6 +85,14 @@ "statickeyauth": { "anonymousCidr": "{{ DNET }}/24", "apiKey": "{{ SENSORONIKEY }}" + }, + "staticrbac": { + "roleFiles": [ + "rbac/permissions", + "rbac/roles", + "rbac/users_roles", + "rbac/custom_roles" + ] } }, "client": { diff --git a/salt/soc/init.sls b/salt/soc/init.sls index b8cdb09ba..c3c466849 100644 --- a/salt/soc/init.sls +++ b/salt/soc/init.sls @@ -62,6 +62,15 @@ soccustom: - mode: 600 - template: jinja +soccustomroles: + file.managed: + - name: /opt/so/conf/soc/custom_roles + - source: salt://soc/files/soc/custom_roles + - user: 939 + - group: 939 + - mode: 600 + - template: jinja + # we dont want this added too early in setup, so we add the onlyif to verify 'startup_states: highstate' # is in the minion config. That line is added before the final highstate during setup sosyncusers: @@ -81,6 +90,8 @@ so-soc: - /opt/so/conf/soc/motd.md:/opt/sensoroni/html/motd.md:ro - /opt/so/conf/soc/banner.md:/opt/sensoroni/html/login/banner.md:ro - /opt/so/conf/soc/custom.js:/opt/sensoroni/html/js/custom.js:ro + - /opt/so/conf/soc/custom_roles:/opt/sensoroni/rbac/custom_roles:ro + - /opt/so/conf/elasticsearch/users_roles:/opt/sensoroni/rbac/users_roles:ro - /opt/so/log/soc/:/opt/sensoroni/logs/:rw {%- if salt['pillar.get']('nodestab', {}) %} - extra_hosts: diff --git a/setup/so-functions b/setup/so-functions index 9a64a561e..d851c80b1 100755 --- a/setup/so-functions +++ b/setup/so-functions @@ -121,7 +121,7 @@ add_web_user() { { echo "Attempting to add administrator user for web interface..."; export SKIP_STATE_APPLY=true - echo "$WEBPASSWD1" | /usr/sbin/so-user add "$WEBUSER"; + echo "$WEBPASSWD1" | /usr/sbin/so-user add "$WEBUSER" "superuser"; unset SKIP_STATE_APPLY echo "Add user result: $?"; } >> "/root/so-user-add.log" 2>&1 From c4d402d8b429a8da407b8557a2e567b45bdac1c1 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Thu, 2 Sep 2021 15:45:47 -0400 Subject: [PATCH 2/8] Ensure role file exists before ES state is run --- salt/common/tools/sbin/so-user | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/salt/common/tools/sbin/so-user b/salt/common/tools/sbin/so-user index 7ec094efb..d60b04567 100755 --- a/salt/common/tools/sbin/so-user +++ b/salt/common/tools/sbin/so-user @@ -150,6 +150,23 @@ function createElasticFile() { chown "${esUID}:${esGID}" "$filename" } +function ensureRoleFileExists() { + if [ ! -f "$elasticRolesFile" ]; then + echo "Creating new roles file: $elasticRolesFile" + rolesTmpFile="${elasticRolesFile}.tmp" + createElasticFile "${rolesTmpFile}" + authPillarJson=$(lookup_salt_value "auth" "elasticsearch" "pillar" "json") + syncElasticSystemRole "$authPillarJson" "so_elastic_user" "superuser" "$rolesTmpFile" + syncElasticSystemRole "$authPillarJson" "so_kibana_user" "superuser" "$rolesTmpFile" + syncElasticSystemRole "$authPillarJson" "so_logstash_user" "superuser" "$rolesTmpFile" + syncElasticSystemRole "$authPillarJson" "so_beats_user" "superuser" "$rolesTmpFile" + syncElasticSystemRole "$authPillarJson" "so_monitor_user" "remote_monitoring_collector" "$rolesTmpFile" + syncElasticSystemRole "$authPillarJson" "so_monitor_user" "remote_monitoring_agent" "$rolesTmpFile" + syncElasticSystemRole "$authPillarJson" "so_monitor_user" "monitoring_user" "$rolesTmpFile" + mv "${rolesTmpFile}" "${elasticRolesFile}" + fi +} + function syncElasticSystemUser() { json=$1 userid=$2 @@ -179,6 +196,8 @@ function syncElasticSystemRole() { function syncElastic() { echo "Syncing users between SOC and Elastic..." + ensureRoleFileExists + usersTmpFile="${elasticUsersFile}.tmp" createElasticFile "${usersTmpFile}" @@ -263,19 +282,7 @@ function adjustUserRole() { identityId=$(findIdByEmail "$email") [[ ${identityId} == "" ]] && fail "User not found" - if [ ! -f "$filename" ]; then - rolesTmpFile="${elasticRolesFile}.tmp" - createElasticFile "${rolesTmpFile}" - authPillarJson=$(lookup_salt_value "auth" "elasticsearch" "pillar" "json") - syncElasticSystemRole "$authPillarJson" "so_elastic_user" "superuser" "$rolesTmpFile" - syncElasticSystemRole "$authPillarJson" "so_kibana_user" "superuser" "$rolesTmpFile" - syncElasticSystemRole "$authPillarJson" "so_logstash_user" "superuser" "$rolesTmpFile" - syncElasticSystemRole "$authPillarJson" "so_beats_user" "superuser" "$rolesTmpFile" - syncElasticSystemRole "$authPillarJson" "so_monitor_user" "remote_monitoring_collector" "$rolesTmpFile" - syncElasticSystemRole "$authPillarJson" "so_monitor_user" "remote_monitoring_agent" "$rolesTmpFile" - syncElasticSystemRole "$authPillarJson" "so_monitor_user" "monitoring_user" "$rolesTmpFile" - mv "${rolesTmpFile}" "${elasticRolesFile}" - fi + ensureRoleFileExists filename="$elasticRolesFile" grep "$role:" "$elasticRolesFile" | grep "$email" && hasRole=1 From ce70380f0ff4d19130709b96e7d7b4bd4edf39f8 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Thu, 2 Sep 2021 17:59:33 -0400 Subject: [PATCH 3/8] resolve so-user errors from recent auth changes --- salt/common/tools/sbin/so-user | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/salt/common/tools/sbin/so-user b/salt/common/tools/sbin/so-user index d60b04567..d7f9c2e6b 100755 --- a/salt/common/tools/sbin/so-user +++ b/salt/common/tools/sbin/so-user @@ -20,7 +20,7 @@ source $(dirname $0)/so-common DEFAULT_ROLE=analyst -if [[ $# -lt 1 || $# -gt 2 ]]; then +if [[ $# -lt 1 || $# -gt 3 ]]; then echo "Usage: $0 [email] [role]" echo "" echo " where is one of the following:" @@ -197,7 +197,7 @@ function syncElasticSystemRole() { function syncElastic() { echo "Syncing users between SOC and Elastic..." ensureRoleFileExists - + usersTmpFile="${elasticUsersFile}.tmp" createElasticFile "${usersTmpFile}" @@ -255,7 +255,7 @@ function listUsers() { users=$(echo "${response}" | jq -r ".[] | .verifiable_addresses[0].value" | sort) for user in $users; do - roles=$(grep "$user" users_roles | cut -d: -f1 | tr '\n' ' ') + roles=$(grep "$user" "$elasticRolesFile" | cut -d: -f1 | tr '\n' ' ') echo "$user: $roles" done } @@ -264,14 +264,14 @@ function addUserRole() { email=$1 role=$2 - return adjustUserRole "$email" "$role" "add" + adjustUserRole "$email" "$role" "add" } function deleteUserRole() { email=$1 role=$2 - return adjustUserRole "$email" "$role" "del" + adjustUserRole "$email" "$role" "del" } function adjustUserRole() { @@ -285,9 +285,10 @@ function adjustUserRole() { ensureRoleFileExists filename="$elasticRolesFile" - grep "$role:" "$elasticRolesFile" | grep "$email" && hasRole=1 + hasRole=0 + grep "$role:" "$elasticRolesFile" | grep -q "$email" && hasRole=1 if [[ "$op" == "add" ]]; then - if [[ "$hasRole" -eq 1 ]]; then + if [[ "$hasRole" == "1" ]]; then fail "User '$email' already has the role: $role" else echo "$role:$email" >> "$filename" @@ -299,15 +300,13 @@ function adjustUserRole() { sed -i "/^$role:$email\$/d" "$filename" fi else - echo "Unsupported role adjustment operation: $op" - exit 1 + fail "Unsupported role adjustment operation: $op" fi - return 0 } function createUser() { email=$1 - role=$1 + role=$2 now=$(date -u +%FT%TZ) addUserJson=$(cat < Date: Thu, 2 Sep 2021 19:12:32 -0400 Subject: [PATCH 4/8] Consolidate password validation messaging --- salt/common/tools/sbin/so-common | 9 +++++++++ salt/common/tools/sbin/so-fleet-user-add | 5 +---- salt/common/tools/sbin/so-thehive-user-add | 5 +---- salt/common/tools/sbin/so-user | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/salt/common/tools/sbin/so-common b/salt/common/tools/sbin/so-common index 7ad74ad49..fe97c9b27 100755 --- a/salt/common/tools/sbin/so-common +++ b/salt/common/tools/sbin/so-common @@ -99,6 +99,15 @@ check_password() { return $? } +check_password_and_exit() { + local password=$1 + if ! check_password "$password"; then + echo "Password is invalid. Do not include single quotes, double quotes, dollar signs, and backslashes in the password." + exit 2 + fi + return 0 +} + check_elastic_license() { [ -n "$TESTING" ] && return diff --git a/salt/common/tools/sbin/so-fleet-user-add b/salt/common/tools/sbin/so-fleet-user-add index 9d80c2076..8ce7325c3 100755 --- a/salt/common/tools/sbin/so-fleet-user-add +++ b/salt/common/tools/sbin/so-fleet-user-add @@ -41,10 +41,7 @@ if [[ $? == 0 ]]; then fi read -rs FLEET_PASS -if ! check_password "$FLEET_PASS"; then - echo "Password is invalid. Please exclude single quotes, double quotes, dollar signs, and backslashes from the password." - exit 2 -fi +check_password_and_exit "$FLEET_PASS" FLEET_HASH=$(docker exec so-soctopus python -c "import bcrypt; print(bcrypt.hashpw('$FLEET_PASS'.encode('utf-8'), bcrypt.gensalt()).decode('utf-8'));" 2>&1) if [[ $? -ne 0 ]]; then diff --git a/salt/common/tools/sbin/so-thehive-user-add b/salt/common/tools/sbin/so-thehive-user-add index e26dc58fc..9cbe0cd56 100755 --- a/salt/common/tools/sbin/so-thehive-user-add +++ b/salt/common/tools/sbin/so-thehive-user-add @@ -41,10 +41,7 @@ if [[ $? == 0 ]]; then fi read -rs THEHIVE_PASS -if ! check_password "$THEHIVE_PASS"; then - echo "Password is invalid. Please exclude single quotes, double quotes, dollar signs, and backslashes from the password." - exit 2 -fi +check_password_and_exit "$THEHIVE_PASS" # Create new user in TheHive resp=$(curl -sk -XPOST -H "Authorization: Bearer $THEHIVE_KEY" -H "Content-Type: application/json" -L "https://$THEHVIE_API_URL/user" -d "{\"login\" : \"$THEHIVE_USER\",\"name\" : \"$THEHIVE_USER\",\"roles\" : [\"read\",\"alert\",\"write\",\"admin\"],\"preferences\" : \"{}\",\"password\" : \"$THEHIVE_PASS\"}") diff --git a/salt/common/tools/sbin/so-user b/salt/common/tools/sbin/so-user index d7f9c2e6b..757ca10c1 100755 --- a/salt/common/tools/sbin/so-user +++ b/salt/common/tools/sbin/so-user @@ -98,7 +98,7 @@ function validatePassword() { if [[ $len -lt 6 ]]; then echo "Password does not meet the minimum requirements" exit 2 - fi + check_password_and_exit "$password" } function validateEmail() { From 649f339934628fed1452f84f6c727140bdc4d991 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Thu, 2 Sep 2021 20:30:48 -0400 Subject: [PATCH 5/8] Correct typo --- salt/common/tools/sbin/so-user | 1 + 1 file changed, 1 insertion(+) diff --git a/salt/common/tools/sbin/so-user b/salt/common/tools/sbin/so-user index 757ca10c1..b3d65b128 100755 --- a/salt/common/tools/sbin/so-user +++ b/salt/common/tools/sbin/so-user @@ -98,6 +98,7 @@ function validatePassword() { if [[ $len -lt 6 ]]; then echo "Password does not meet the minimum requirements" exit 2 + fi check_password_and_exit "$password" } From fbbb7f4e85fdc31427e83483b4062c8ee41cfa4d Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Fri, 3 Sep 2021 15:54:05 -0400 Subject: [PATCH 6/8] Add auditor role; update analyst role with correct syntax --- .../tools/sbin/so-elasticsearch-roles-load | 2 +- salt/elasticsearch/roles/analyst.json | 97 +++++++++++-------- 2 files changed, 58 insertions(+), 41 deletions(-) diff --git a/salt/common/tools/sbin/so-elasticsearch-roles-load b/salt/common/tools/sbin/so-elasticsearch-roles-load index 3b0f580fe..d094163ee 100644 --- a/salt/common/tools/sbin/so-elasticsearch-roles-load +++ b/salt/common/tools/sbin/so-elasticsearch-roles-load @@ -51,7 +51,7 @@ cd ${ELASTICSEARCH_ROLES} echo "Loading templates..." for role in *; do name=$(echo "$role" | cut -d. -f1) - so-elasticsearch-query security/roles/$name -XPUT -d @"$role" + so-elasticsearch-query _security/role/$name -XPUT -d @"$role" done cd - >/dev/null diff --git a/salt/elasticsearch/roles/analyst.json b/salt/elasticsearch/roles/analyst.json index f81c5a67e..3fbaa9489 100644 --- a/salt/elasticsearch/roles/analyst.json +++ b/salt/elasticsearch/roles/analyst.json @@ -1,45 +1,62 @@ { - "elasticsearch": { - "cluster": [ - "cancel_task", - "create_snapshot", - "monitor", - "monitor_data_frame_transforms", - "monitor_ml", - "monitor_rollup", - "monitor_snapshot", - "monitor_text_structure", - "monitor_transform", - "monitor_watcher", - "read_ccr", - "read_ilm", - "read_pipeline", - "read_slm" - ], - "indices": [ - { - "names": [ - "so-*" - ], - "privileges": [ - "read", - "read_cross_cluster", - "monitor", - "view_index_metadata" - ] - } - ], - "run_as": [] - }, - "kibana": [ + "cluster": [ + "cancel_task", + "create_snapshot", + "monitor", + "monitor_data_frame_transforms", + "monitor_ml", + "monitor_rollup", + "monitor_snapshot", + "monitor_text_structure", + "monitor_transform", + "monitor_watcher", + "read_ccr", + "read_ilm", + "read_pipeline", + "read_slm" + ], + "indices": [ { - "spaces": [ - "*" + "names": [ + "so-*" ], - "base": [ - "read" - ], - "feature": {} + "privileges": [ + "index", + "read", + "read_cross_cluster", + "monitor", + "view_index_metadata" + ] } - ] + ], + "applications": [ + { + "application": "kibana-.kibana", + "privileges": [ + "feature_discover.all", + "feature_dashboard.all", + "feature_canvas.all", + "feature_maps.all", + "feature_ml.all", + "feature_logs.read", + "feature_visualize.all", + "feature_infrastructure.read", + "feature_apm.read", + "feature_uptime.read", + "feature_siem.read", + "feature_dev_tools.read", + "feature_advancedSettings.read", + "feature_indexPatterns.read", + "feature_savedObjectsManagement.read", + "feature_savedObjectsTagging.read", + "feature_fleet.all", + "feature_actions.read", + "feature_stackAlerts.read" + ], + "resources": [ + "*" + ] + } + ], + "run_as": [] } \ No newline at end of file From 94ea1f856b4ce8cb1db30321a7a866de2ab70475 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Fri, 3 Sep 2021 15:59:48 -0400 Subject: [PATCH 7/8] Add auditor role; update analyst role with correct syntax --- salt/elasticsearch/roles/auditor.json | 59 +++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 salt/elasticsearch/roles/auditor.json diff --git a/salt/elasticsearch/roles/auditor.json b/salt/elasticsearch/roles/auditor.json new file mode 100644 index 000000000..26df1207c --- /dev/null +++ b/salt/elasticsearch/roles/auditor.json @@ -0,0 +1,59 @@ +{ + "cluster": [ + "monitor", + "monitor_data_frame_transforms", + "monitor_ml", + "monitor_rollup", + "monitor_snapshot", + "monitor_text_structure", + "monitor_transform", + "monitor_watcher", + "read_ccr", + "read_ilm", + "read_pipeline", + "read_slm" + ], + "indices": [ + { + "names": [ + "so-*" + ], + "privileges": [ + "read", + "read_cross_cluster", + "monitor", + "view_index_metadata" + ] + } + ], + "applications": [ + { + "application": "kibana-.kibana", + "privileges": [ + "feature_discover.read", + "feature_dashboard.read", + "feature_canvas.read", + "feature_maps.read", + "feature_ml.read", + "feature_logs.read", + "feature_visualize.read", + "feature_infrastructure.read", + "feature_apm.read", + "feature_uptime.read", + "feature_siem.read", + "feature_dev_tools.read", + "feature_advancedSettings.read", + "feature_indexPatterns.read", + "feature_savedObjectsManagement.read", + "feature_savedObjectsTagging.read", + "feature_fleet.read", + "feature_actions.read", + "feature_stackAlerts.read" + ], + "resources": [ + "*" + ] + } + ], + "run_as": [] +} \ No newline at end of file From 3c59579f99ae23398d3d1bf5f12a03e127de7469 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Tue, 7 Sep 2021 13:03:30 -0400 Subject: [PATCH 8/8] Add maintenance privilege for analysts to refresh indices --- salt/elasticsearch/roles/analyst.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/salt/elasticsearch/roles/analyst.json b/salt/elasticsearch/roles/analyst.json index 3fbaa9489..fc788e2f1 100644 --- a/salt/elasticsearch/roles/analyst.json +++ b/salt/elasticsearch/roles/analyst.json @@ -22,9 +22,10 @@ ], "privileges": [ "index", + "maintenance", + "monitor", "read", "read_cross_cluster", - "monitor", "view_index_metadata" ] }