From 0ad1a1a2627afaf71fe1167e2e69ec000982c342 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Mon, 26 Sep 2022 14:57:33 -0400 Subject: [PATCH 1/6] so-user and salt-relay updates for user management --- salt/common/tools/sbin/so-user | 164 +++++++++++++++++++++---- salt/common/tools/sbin/so-user-add | 2 +- salt/common/tools/sbin/so-user-disable | 2 +- salt/common/tools/sbin/so-user-enable | 2 +- salt/soc/files/bin/salt-relay.sh | 118 ++++++++++++++++-- setup/so-functions | 4 +- 6 files changed, 252 insertions(+), 40 deletions(-) diff --git a/salt/common/tools/sbin/so-user b/salt/common/tools/sbin/so-user index 81bfa0d76..74dc14107 100755 --- a/salt/common/tools/sbin/so-user +++ b/salt/common/tools/sbin/so-user @@ -11,29 +11,106 @@ source $(dirname $0)/so-common DEFAULT_ROLE=analyst -if [[ $# -lt 1 || $# -gt 3 ]]; then - 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, while 'role' parameter is optional and defaults to $DEFAULT_ROLE" - 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 and disables MFA; requires 'email' parameter" - echo " enable: Enables a user; requires 'email' parameter" - echo " disable: Disables a user; requires 'email' parameter" - echo " validate: Validates that the given email address and password are acceptable; requires 'email' parameter" - echo " valemail: Validates that the given email address is acceptable; requires 'email' parameter" - echo " valpass: Validates that a password is acceptable" - echo "" - echo " Note that the password can be piped into STDIN to avoid prompting for it" +function usage() { + cat < [supporting parameters]" + + where is one of the following:" + + list: Lists all user email addresses currently defined in the identity system" + + add: Adds a new user to the identity system" + Required parameters: " + --email " + Optional parameters: " + --role (defaults to $DEFAULT_ROLE)" + --firstName (defaults to blank)" + --lastName (defaults to blank)" + --note (defaults to blank)" + + addrole: Grants a role to an existing user" + Required parameters: " + --email " + --role " + + delrole: Removes a role from an existing user" + Required parameters: " + --email " + --role " + + password: Updates a user's password and disables MFA" + Required parameters: " + --email " + + profile: Updates a user's profile information" + Required parameters: " + --email " + Optional parameters: " + --role (defaults to $DEFAULT_ROLE)" + --firstName (defaults to blank)" + --lastName (defaults to blank)" + --note (defaults to blank)" + + enable: Enables a user" + Required parameters: " + --email " + + disable: Disables a user" + Required parameters: " + --email " + + validate: Validates that the given email address and password are acceptable" + Required parameters: " + --email " + + valemail: Validates that the given email address is acceptable; requires 'email' parameter" + Required parameters: " + --email " + + valpass: Validates that a password is acceptable" + + Note that the password can be piped into STDIN to avoid prompting for it" +USAGE_EOF exit 1 +} + +if [[ $# -lt 1 || $1 == --help || $1 == -h || $1 == -? || $1 == --h ]]; then + usage fi operation=$1 -email=$2 -role=$3 +shift + +while [[ $# -gt 0 ]]; do + param=$1 + shift + case "$param" in + --email) + email=$1 + shift + ;; + --role) + role=$1 + shift + ;; + --firstName) + firstName=$1 + shift + ;; + --lastName) + lastName=$1 + shift + ;; + --note) + note=$1 + shift + ;; + *) + echo "Encountered unexpected parameter: $param" + usage + ;; + esac +done kratosUrl=${KRATOS_URL:-http://127.0.0.1:4434/admin} databasePath=${KRATOS_DB_PATH:-/opt/so/conf/kratos/db/db.sqlite} @@ -369,11 +446,19 @@ function adjustUserRole() { function createUser() { email=$1 role=$2 + firstName=$3 + lastName=$4 + note=$5 now=$(date -u +%FT%TZ) addUserJson=$(cat < "${SOC_PIPE}") + else + log "Unsuccessful command execution: $response ($exit_code)" + $(echo "false" > "${SOC_PIPE}") + fi +} + +function manage_salt() { + request=$1 + op=$(echo "$request" | jq -r .operation) + + case "$op" in + state) + state=$(echo "$request" | jq -r .state) + response=$(salt-call state.apply "$state" queue=True) + exit_code=$? + ;; + highstate) + response=$(salt-call state.highstate queue=True) + exit_code=$? + ;; + *) + response="Unsupported salt operation: $op" + exit_code=1 + ;; + esac + + if [[ exit_code -eq 0 ]]; then + log "Successful command execution" + $(echo "true" > "${SOC_PIPE}") + else + log "Unsuccessful command execution: $response ($exit_code)" + $(echo "false" > "${SOC_PIPE}") + fi +} + while true; do log "Listening for request" request=$(cat ${SOC_PIPE}) if [[ "$request" != "" ]]; then - log "Received request: ${request}" - case "$request" in + command=$(echo "$request" | jq -r .command) + log "Received request; command=${command}" + case "$command" in list-minions) list_minions ;; - manage-minion*) - manage_minion ${request} + manage-minion) + manage_minion "${request}" + ;; + manage-user) + manage_user "${request}" + ;; + manage-salt) + manage_salt "${request}" ;; *) - log "Unsupported command: $request" + log "Unsupported command: $command" $(echo "false" > "${SOC_PIPE}") + ;; esac # allow remote reader to get a clean reader before we try to read again on next loop diff --git a/setup/so-functions b/setup/so-functions index 5a0e35be0..7dfa7b047 100755 --- a/setup/so-functions +++ b/setup/so-functions @@ -69,7 +69,7 @@ add_web_user() { { info "Attempting to add administrator user for web interface..."; export SKIP_STATE_APPLY=true - echo "$WEBPASSWD1" | /usr/sbin/so-user add "$WEBUSER" "superuser"; + echo "$WEBPASSWD1" | /usr/sbin/so-user add --email "$WEBUSER" --role "superuser"; unset SKIP_STATE_APPLY info "Add user result: $?"; } >> "/root/so-user-add.log" 2>&1 @@ -550,7 +550,7 @@ collect_so_allow() { collect_webuser_inputs() { whiptail_create_web_user - while ! so-user valemail "$WEBUSER" >> "$setup_log" 2>&1; do + while ! so-user valemail --email "$WEBUSER" >> "$setup_log" 2>&1; do whiptail_invalid_user_warning whiptail_create_web_user "$WEBUSER" done From 8e175b2d3f6e635900fc568aa2952e66fb0d8d95 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Tue, 27 Sep 2022 07:05:04 -0400 Subject: [PATCH 2/6] add manual sync --- salt/common/tools/sbin/so-user | 16 ++++++++++++++++ salt/soc/files/bin/salt-relay.sh | 20 +++++++++++++------- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/salt/common/tools/sbin/so-user b/salt/common/tools/sbin/so-user index 74dc14107..2c6a28914 100755 --- a/salt/common/tools/sbin/so-user +++ b/salt/common/tools/sbin/so-user @@ -27,20 +27,27 @@ function usage() { --firstName (defaults to blank)" --lastName (defaults to blank)" --note (defaults to blank)" + --skip-sync (defers the Elastic sync until the next scheduled time) addrole: Grants a role to an existing user" Required parameters: " --email " --role " + Optional parameters: " + --skip-sync (defers the Elastic sync until the next scheduled time) delrole: Removes a role from an existing user" Required parameters: " --email " --role " + Optional parameters: " + --skip-sync (defers the Elastic sync until the next scheduled time) password: Updates a user's password and disables MFA" Required parameters: " --email " + Optional parameters: " + --skip-sync (defers the Elastic sync until the next scheduled time) profile: Updates a user's profile information" Required parameters: " @@ -54,10 +61,14 @@ function usage() { enable: Enables a user" Required parameters: " --email " + Optional parameters: " + --skip-sync (defers the Elastic sync until the next scheduled time) disable: Disables a user" Required parameters: " --email " + Optional parameters: " + --skip-sync (defers the Elastic sync until the next scheduled time) validate: Validates that the given email address and password are acceptable" Required parameters: " @@ -105,6 +116,9 @@ while [[ $# -gt 0 ]]; do note=$1 shift ;; + --skip-sync) + SKIP_SYNC=1 + ;; *) echo "Encountered unexpected parameter: $param" usage @@ -290,6 +304,8 @@ function syncElasticSystemRole() { } function syncElastic() { + [[ -n $SKIP_SYNC ]] && return + echo "Syncing users and roles between SOC and Elastic..." usersTmpFile="${elasticUsersFile}.tmp" diff --git a/salt/soc/files/bin/salt-relay.sh b/salt/soc/files/bin/salt-relay.sh index 752b1b1ad..c912cbeff 100755 --- a/salt/soc/files/bin/salt-relay.sh +++ b/salt/soc/files/bin/salt-relay.sh @@ -55,7 +55,6 @@ function manage_minion() { function manage_user() { request=$1 op=$(echo "$request" | jq -r .operation) - email=$(echo "$request" | jq -r .email) case "$op" in add) @@ -66,27 +65,27 @@ function manage_user() { lastName=$(echo "$request" | jq -r .lastName) note=$(echo "$request" | jq -r .note) log "Performing user '$op' for user '$email' with firstname '$firstName', lastname '$lastName', note '$note' and role '$role'" - response=$(echo "$password" | so-user "$op" --email "$email" --firstName "$firstName" --lastName "$lastName" --note "$note" --role "$role") + response=$(echo "$password" | so-user "$op" --email "$email" --firstName "$firstName" --lastName "$lastName" --note "$note" --role "$role" --skip-sync) exit_code=$? ;; add|enable|disable|delete) email=$(echo "$request" | jq -r .email) log "Performing user '$op' for user '$email'" - response=$(so-user "$op" --email "$email") + response=$(so-user "$op" --email "$email" --skip-sync) exit_code=$? ;; addrole|delrole) email=$(echo "$request" | jq -r .email) role=$(echo "$request" | jq -r .role) log "Performing '$op' for user '$email' with role '$role'" - response=$(so-user "$op" --email "$email" --role "$role") + response=$(so-user "$op" --email "$email" --role "$role" --skip-sync) exit_code=$? ;; password) email=$(echo "$request" | jq -r .email) password=$(echo "$request" | jq -r .password) log "Performing '$op' operation for user '$email'" - response=$(echo "$password" | so-user "$op" --email "$email") + response=$(echo "$password" | so-user "$op" --email "$email" --skip-sync) exit_code=$? ;; profile) @@ -98,6 +97,11 @@ function manage_user() { response=$(so-user "$op" --email "$email" --firstName "$firstName" --lastName "$lastName" --note "$note") exit_code=$? ;; + sync) + log "Performing '$op'" + response=$(so-user "$op") + exit_code=$? + ;; *) response="Unsupported user operation: $op" exit_code=1 @@ -119,12 +123,14 @@ function manage_salt() { case "$op" in state) + log "Performing '$op' for '$state'" state=$(echo "$request" | jq -r .state) - response=$(salt-call state.apply "$state" queue=True) + response=$(salt '*' state.apply "$state" queue=True) exit_code=$? ;; highstate) - response=$(salt-call state.highstate queue=True) + log "Performing '$op'" + response=$(salt '*' state.highstate queue=True) exit_code=$? ;; *) From 556ddc2ee47f658117c3b3518607f3ab42460b56 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Tue, 27 Sep 2022 09:24:34 -0400 Subject: [PATCH 3/6] sync in background --- salt/common/tools/sbin/so-user | 4 ++-- salt/soc/files/bin/salt-relay.sh | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/salt/common/tools/sbin/so-user b/salt/common/tools/sbin/so-user index 2c6a28914..0a287aa7c 100755 --- a/salt/common/tools/sbin/so-user +++ b/salt/common/tools/sbin/so-user @@ -370,9 +370,9 @@ function syncElastic() { mv "${rolesTmpFile}" "${elasticRolesFile}" if [[ -z "$SKIP_STATE_APPLY" ]]; then - echo "Elastic state will be re-applied to affected minions. This may take several minutes..." + echo "Elastic state will be re-applied to affected minions. This will run in the background and may take several minutes to complete." echo "Applying elastic state to elastic minions at $(date)" >> /opt/so/log/soc/sync.log 2>&1 - salt -C 'G@role:so-standalone or G@role:so-eval or G@role:so-import or G@role:so-manager or G@role:so-managersearch or G@role:so-searchnode or G@role:so-heavynode' state.apply elasticsearch queue=True >> /opt/so/log/soc/sync.log 2>&1 + salt --async -C 'G@role:so-standalone or G@role:so-eval or G@role:so-import or G@role:so-manager or G@role:so-managersearch or G@role:so-searchnode or G@role:so-heavynode' state.apply elasticsearch queue=True >> /opt/so/log/soc/sync.log 2>&1 fi else echo "Newly generated users/roles files are incomplete; aborting." diff --git a/salt/soc/files/bin/salt-relay.sh b/salt/soc/files/bin/salt-relay.sh index c912cbeff..386b91612 100755 --- a/salt/soc/files/bin/salt-relay.sh +++ b/salt/soc/files/bin/salt-relay.sh @@ -120,17 +120,21 @@ function manage_user() { function manage_salt() { request=$1 op=$(echo "$request" | jq -r .operation) + minion=$(echo "$request" | jq -r .minion) + if [[ -s $minion ]]; then + minion=$(cat /etc/salt/minion | grep "id:" | awk '{print $2}') + fi case "$op" in state) - log "Performing '$op' for '$state'" + log "Performing '$op' for '$state' on minion '$minion'" state=$(echo "$request" | jq -r .state) - response=$(salt '*' state.apply "$state" queue=True) + response=$(salt --async $minion state.apply "$state" queue=True) exit_code=$? ;; highstate) - log "Performing '$op'" - response=$(salt '*' state.highstate queue=True) + log "Performing '$op' on minion '$minion'" + response=$(salt --async $minion state.highstate queue=True) exit_code=$? ;; *) From 7f7f2c15d0bf6b88817b8213ec3208a82a1e5f19 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Tue, 27 Sep 2022 10:29:21 -0400 Subject: [PATCH 4/6] add support for querying active salt jobs (future use) --- salt/soc/files/bin/salt-relay.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/salt/soc/files/bin/salt-relay.sh b/salt/soc/files/bin/salt-relay.sh index 386b91612..eec0949d1 100755 --- a/salt/soc/files/bin/salt-relay.sh +++ b/salt/soc/files/bin/salt-relay.sh @@ -121,7 +121,7 @@ function manage_salt() { request=$1 op=$(echo "$request" | jq -r .operation) minion=$(echo "$request" | jq -r .minion) - if [[ -s $minion ]]; then + if [[ -s $minion || "$minion" == "null" ]]; then minion=$(cat /etc/salt/minion | grep "id:" | awk '{print $2}') fi @@ -137,6 +137,12 @@ function manage_salt() { response=$(salt --async $minion state.highstate queue=True) exit_code=$? ;; + activejobs) + log "Querying active salt jobs" + response=$(salt-run jobs.active -out json -l quiet) + $(echo "$response" > "${SOC_PIPE}") + return + ;; *) response="Unsupported salt operation: $op" exit_code=1 From 851e44e5fa5c1faff7517575f16bedd392ed5a20 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Tue, 27 Sep 2022 10:31:14 -0400 Subject: [PATCH 5/6] ensure salt-relay is restarted when SOC is manually restarted --- salt/common/tools/sbin/so-soc-restart | 1 + 1 file changed, 1 insertion(+) diff --git a/salt/common/tools/sbin/so-soc-restart b/salt/common/tools/sbin/so-soc-restart index 9d252e2c1..0a678de96 100755 --- a/salt/common/tools/sbin/so-soc-restart +++ b/salt/common/tools/sbin/so-soc-restart @@ -9,4 +9,5 @@ . /usr/sbin/so-common +pkill salt-relay.sh /usr/sbin/so-restart soc $1 From 53b4f01921db415d3f225e701e2b94ab683cb421 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Tue, 27 Sep 2022 10:54:08 -0400 Subject: [PATCH 6/6] replace quotes on minion arg --- salt/soc/files/bin/salt-relay.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/salt/soc/files/bin/salt-relay.sh b/salt/soc/files/bin/salt-relay.sh index eec0949d1..bc6f70e51 100755 --- a/salt/soc/files/bin/salt-relay.sh +++ b/salt/soc/files/bin/salt-relay.sh @@ -122,19 +122,19 @@ function manage_salt() { op=$(echo "$request" | jq -r .operation) minion=$(echo "$request" | jq -r .minion) if [[ -s $minion || "$minion" == "null" ]]; then - minion=$(cat /etc/salt/minion | grep "id:" | awk '{print $2}') + minion=$(cat /etc/salt/minion | grep "id:" | awk '{print $2}' | sed "s/'//g") fi case "$op" in state) log "Performing '$op' for '$state' on minion '$minion'" state=$(echo "$request" | jq -r .state) - response=$(salt --async $minion state.apply "$state" queue=True) + response=$(salt --async "$minion" state.apply "$state" queue=True) exit_code=$? ;; highstate) - log "Performing '$op' on minion '$minion'" - response=$(salt --async $minion state.highstate queue=True) + log "Performing '$op' on minion $minion" + response=$(salt --async "$minion" state.highstate queue=True) exit_code=$? ;; activejobs)