mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2025-12-08 10:12:53 +01:00
Merge pull request #8822 from Security-Onion-Solutions/config
user management / sync
This commit is contained in:
@@ -9,4 +9,5 @@
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
pkill salt-relay.sh
|
||||
/usr/sbin/so-restart soc $1
|
||||
|
||||
@@ -11,29 +11,120 @@ source $(dirname $0)/so-common
|
||||
|
||||
DEFAULT_ROLE=analyst
|
||||
|
||||
if [[ $# -lt 1 || $# -gt 3 ]]; then
|
||||
echo "Usage: $0 <operation> [email] [role]"
|
||||
echo ""
|
||||
echo " where <operation> 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 <<USAGE_EOF
|
||||
Usage: $0 <operation> [supporting parameters]"
|
||||
|
||||
where <operation> 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 <email>"
|
||||
Optional parameters: "
|
||||
--role <role> (defaults to $DEFAULT_ROLE)"
|
||||
--firstName <firstName> (defaults to blank)"
|
||||
--lastName <lastName> (defaults to blank)"
|
||||
--note <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 <email>"
|
||||
--role <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 <email>"
|
||||
--role <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 <email>"
|
||||
Optional parameters: "
|
||||
--skip-sync (defers the Elastic sync until the next scheduled time)
|
||||
|
||||
profile: Updates a user's profile information"
|
||||
Required parameters: "
|
||||
--email <email>"
|
||||
Optional parameters: "
|
||||
--role <role> (defaults to $DEFAULT_ROLE)"
|
||||
--firstName <firstName> (defaults to blank)"
|
||||
--lastName <lastName> (defaults to blank)"
|
||||
--note <note> (defaults to blank)"
|
||||
|
||||
enable: Enables a user"
|
||||
Required parameters: "
|
||||
--email <email>"
|
||||
Optional parameters: "
|
||||
--skip-sync (defers the Elastic sync until the next scheduled time)
|
||||
|
||||
disable: Disables a user"
|
||||
Required parameters: "
|
||||
--email <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: "
|
||||
--email <email>"
|
||||
|
||||
valemail: Validates that the given email address is acceptable; requires 'email' parameter"
|
||||
Required parameters: "
|
||||
--email <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
|
||||
;;
|
||||
--skip-sync)
|
||||
SKIP_SYNC=1
|
||||
;;
|
||||
*)
|
||||
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}
|
||||
@@ -213,6 +304,8 @@ function syncElasticSystemRole() {
|
||||
}
|
||||
|
||||
function syncElastic() {
|
||||
[[ -n $SKIP_SYNC ]] && return
|
||||
|
||||
echo "Syncing users and roles between SOC and Elastic..."
|
||||
|
||||
usersTmpFile="${elasticUsersFile}.tmp"
|
||||
@@ -277,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."
|
||||
@@ -369,11 +462,19 @@ function adjustUserRole() {
|
||||
function createUser() {
|
||||
email=$1
|
||||
role=$2
|
||||
firstName=$3
|
||||
lastName=$4
|
||||
note=$5
|
||||
|
||||
now=$(date -u +%FT%TZ)
|
||||
addUserJson=$(cat <<EOF
|
||||
{
|
||||
"traits": {"email":"${email}"},
|
||||
"traits": {
|
||||
"email": "${email}",
|
||||
"firstName": "${firstName}",
|
||||
"lastName": "${lastName}",
|
||||
"note": "${note}"
|
||||
},
|
||||
"schema_id": "default"
|
||||
}
|
||||
EOF
|
||||
@@ -432,7 +533,7 @@ function updateStatus() {
|
||||
[[ $? != 0 ]] && fail "Unable to update user"
|
||||
}
|
||||
|
||||
function updateUser() {
|
||||
function updateUserPassword() {
|
||||
email=$1
|
||||
|
||||
identityId=$(findIdByEmail "$email")
|
||||
@@ -441,6 +542,25 @@ function updateUser() {
|
||||
updatePassword "$identityId"
|
||||
}
|
||||
|
||||
function updateUserProfile() {
|
||||
email=$1
|
||||
|
||||
identityId=$(findIdByEmail "$email")
|
||||
[[ ${identityId} == "" ]] && fail "User not found"
|
||||
|
||||
response=$(curl -Ss -L "${kratosUrl}/identities/$identityId")
|
||||
[[ $? != 0 ]] && fail "Unable to communicate with Kratos"
|
||||
|
||||
schemaId=$(echo "$response" | jq -r .schema_id)
|
||||
state=$(echo "$response" | jq -r .state)
|
||||
|
||||
traitBlock="{\"email\":\"$email\",\"firstName\":\"$firstName\",\"lastName\":\"$lastName\",\"note\":\"$note\"}"
|
||||
|
||||
body="{ \"schema_id\": \"$schemaId\", \"state\": \"$state\", \"traits\": $traitBlock }"
|
||||
response=$(curl -fSsL -XPUT -H "Content-Type: application/json" "${kratosUrl}/identities/$identityId" -d "$body")
|
||||
[[ $? != 0 ]] && fail "Unable to update user"
|
||||
}
|
||||
|
||||
function deleteUser() {
|
||||
email=$1
|
||||
|
||||
@@ -464,7 +584,7 @@ case "${operation}" in
|
||||
lock
|
||||
validateEmail "$email"
|
||||
updatePassword
|
||||
createUser "$email" "${role:-$DEFAULT_ROLE}"
|
||||
createUser "$email" "${role:-$DEFAULT_ROLE}" "${firstName}" "${lastName}" "${note}"
|
||||
syncAll
|
||||
echo "Successfully added new user to SOC"
|
||||
check_container fleet && echo "$password" | so-fleet-user-add "$email"
|
||||
@@ -500,14 +620,23 @@ case "${operation}" in
|
||||
echo "Successfully removed role from user"
|
||||
;;
|
||||
|
||||
"update")
|
||||
"password")
|
||||
verifyEnvironment
|
||||
[[ "$email" == "" ]] && fail "Email address must be provided"
|
||||
|
||||
lock
|
||||
updateUser "$email"
|
||||
updateUserPassword "$email"
|
||||
syncAll
|
||||
echo "Successfully updated user"
|
||||
echo "Successfully updated user password"
|
||||
;;
|
||||
|
||||
"profile")
|
||||
verifyEnvironment
|
||||
[[ "$email" == "" ]] && fail "Email address must be provided"
|
||||
|
||||
lock
|
||||
updateUserProfile "$email"
|
||||
echo "Successfully updated user profile"
|
||||
;;
|
||||
|
||||
"enable")
|
||||
@@ -571,6 +700,7 @@ case "${operation}" in
|
||||
|
||||
*)
|
||||
fail "Unsupported operation: $operation"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#!/bin/bash
|
||||
so-user add $*
|
||||
so-user add --email $1
|
||||
@@ -1,2 +1,2 @@
|
||||
#!/bin/bash
|
||||
so-user disable $*
|
||||
so-user disable --email $1
|
||||
@@ -1,2 +1,2 @@
|
||||
#!/bin/bash
|
||||
so-user enable $*
|
||||
so-user enable --email $1
|
||||
@@ -37,11 +37,11 @@ function list_minions() {
|
||||
}
|
||||
|
||||
function manage_minion() {
|
||||
command=$1
|
||||
op=$2
|
||||
minion=$3
|
||||
request=$1
|
||||
op=$(echo "$request" | jq -r .operation)
|
||||
id=$(echo "$request" | jq -r .id)
|
||||
|
||||
response=$(so-minion "-o=$op" "-m=$minion")
|
||||
response=$(so-minion "-o=$op" "-m=$id")
|
||||
exit_code=$?
|
||||
if [[ exit_code -eq 0 ]]; then
|
||||
log "Successful command execution"
|
||||
@@ -52,21 +52,135 @@ function manage_minion() {
|
||||
fi
|
||||
}
|
||||
|
||||
function manage_user() {
|
||||
request=$1
|
||||
op=$(echo "$request" | jq -r .operation)
|
||||
|
||||
case "$op" in
|
||||
add)
|
||||
email=$(echo "$request" | jq -r .email)
|
||||
password=$(echo "$request" | jq -r .password)
|
||||
role=$(echo "$request" | jq -r .role)
|
||||
firstName=$(echo "$request" | jq -r .firstName)
|
||||
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" --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" --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" --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" --skip-sync)
|
||||
exit_code=$?
|
||||
;;
|
||||
profile)
|
||||
email=$(echo "$request" | jq -r .email)
|
||||
firstName=$(echo "$request" | jq -r .firstName)
|
||||
lastName=$(echo "$request" | jq -r .lastName)
|
||||
note=$(echo "$request" | jq -r .note)
|
||||
log "Performing '$op' update for user '$email' with firstname '$firstName', lastname '$lastName', and note '$note'"
|
||||
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
|
||||
;;
|
||||
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
|
||||
}
|
||||
|
||||
function manage_salt() {
|
||||
request=$1
|
||||
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}' | 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)
|
||||
exit_code=$?
|
||||
;;
|
||||
highstate)
|
||||
log "Performing '$op' on minion $minion"
|
||||
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
|
||||
;;
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user