mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2025-12-06 09:12:45 +01:00
* Consistent ending punctuation * Consistent capitalization * Correct comparison operators
275 lines
8.5 KiB
Bash
Executable File
275 lines
8.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# Copyright 2020 Security Onion Solutions. All rights reserved.
|
|
#
|
|
# This program is distributed under the terms of version 2 of the
|
|
# GNU General Public License. See LICENSE for further details.
|
|
#
|
|
# 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.
|
|
|
|
. /usr/sbin/so-common
|
|
|
|
if [[ $# -lt 1 || $# -gt 2 ]]; then
|
|
echo "Usage: $0 <list|add|update|enable|disable|validate|valemail|valpass> [email]"
|
|
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 " 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"
|
|
echo " validate: Validates that the given email address and password are acceptable for defining a new user; requires 'email' parameter"
|
|
echo " valemail: Validates that the given email address is acceptable for defining a new user; requires 'email' parameter"
|
|
echo " valpass: Validates that a password is acceptable for defining a new user"
|
|
echo ""
|
|
echo " Note that the password can be piped into STDIN to avoid prompting for it"
|
|
exit 1
|
|
fi
|
|
|
|
operation=$1
|
|
email=$2
|
|
|
|
kratosUrl=${KRATOS_URL:-http://127.0.0.1:4434}
|
|
databasePath=${KRATOS_DB_PATH:-/opt/so/conf/kratos/db/db.sqlite}
|
|
argon2Iterations=${ARGON2_ITERATIONS:-3}
|
|
argon2Memory=${ARGON2_MEMORY:-14}
|
|
argon2Parallelism=${ARGON2_PARALLELISM:-2}
|
|
argon2HashSize=${ARGON2_HASH_SIZE:-32}
|
|
|
|
function fail() {
|
|
msg=$1
|
|
echo "$1"
|
|
exit 1
|
|
}
|
|
|
|
function require() {
|
|
cmd=$1
|
|
which "$1" 2>&1 > /dev/null
|
|
[[ $? != 0 ]] && fail "This script requires the following command be installed: ${cmd}"
|
|
}
|
|
|
|
# Verify this environment is capable of running this script
|
|
function verifyEnvironment() {
|
|
require "argon2"
|
|
require "jq"
|
|
require "curl"
|
|
require "openssl"
|
|
require "sqlite3"
|
|
[[ ! -f $databasePath ]] && fail "Unable to find database file; specify path via KRATOS_DB_PATH environment variable"
|
|
response=$(curl -Ss ${kratosUrl}/)
|
|
[[ "$response" != "404 page not found" ]] && fail "Unable to communicate with Kratos; specify URL via KRATOS_URL environment variable"
|
|
}
|
|
|
|
function findIdByEmail() {
|
|
email=$1
|
|
|
|
response=$(curl -Ss ${kratosUrl}/identities)
|
|
identityId=$(echo "${response}" | jq ".[] | select(.verifiable_addresses[0].value == \"$email\") | .id")
|
|
echo $identityId
|
|
}
|
|
|
|
function validatePassword() {
|
|
password=$1
|
|
|
|
len=$(expr length "$password")
|
|
if [[ $len -lt 6 ]]; then
|
|
echo "Password does not meet the minimum requirements"
|
|
exit 2
|
|
fi
|
|
}
|
|
|
|
function validateEmail() {
|
|
email=$1
|
|
# (?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])
|
|
if [[ ! "$email" =~ ^[[:alnum:]._%+-]+@[[:alnum:].-]+\.[[:alpha:]]{2,}$ ]]; then
|
|
echo "Email address is invalid"
|
|
exit 3
|
|
fi
|
|
}
|
|
|
|
function updatePassword() {
|
|
identityId=$1
|
|
|
|
if [ -z "$password" ]; then
|
|
# Read password from stdin (show prompt only if no stdin was piped in)
|
|
test -t 0
|
|
if [[ $? == 0 ]]; then
|
|
echo "Enter new password:"
|
|
fi
|
|
read -rs password
|
|
|
|
validatePassword "$password"
|
|
fi
|
|
|
|
if [[ -n $identityId ]]; then
|
|
# Generate password hash
|
|
salt=$(openssl rand -hex 8)
|
|
passwordHash=$(echo "${password}" | argon2 ${salt} -id -t $argon2Iterations -m $argon2Memory -p $argon2Parallelism -l $argon2HashSize -e)
|
|
|
|
# Update DB with new hash
|
|
echo "update identity_credentials set config=CAST('{\"hashed_password\":\"${passwordHash}\"}' as BLOB) where identity_id=${identityId};" | sqlite3 "$databasePath"
|
|
[[ $? != 0 ]] && fail "Unable to update password"
|
|
fi
|
|
}
|
|
|
|
function listUsers() {
|
|
response=$(curl -Ss ${kratosUrl}/identities)
|
|
[[ $? != 0 ]] && fail "Unable to communicate with Kratos"
|
|
|
|
echo "${response}" | jq -r ".[] | .verifiable_addresses[0].value" | sort
|
|
}
|
|
|
|
function createUser() {
|
|
email=$1
|
|
|
|
now=$(date -u +%FT%TZ)
|
|
addUserJson=$(cat <<EOF
|
|
{
|
|
"traits": {"email":"${email}"},
|
|
"schema_id": "default"
|
|
}
|
|
EOF
|
|
)
|
|
|
|
response=$(curl -Ss ${kratosUrl}/identities -d "$addUserJson")
|
|
[[ $? != 0 ]] && fail "Unable to communicate with Kratos"
|
|
|
|
identityId=$(echo "${response}" | jq ".id")
|
|
if [[ ${identityId} == "null" ]]; then
|
|
code=$(echo "${response}" | jq ".error.code")
|
|
[[ "${code}" == "409" ]] && fail "User already exists"
|
|
|
|
reason=$(echo "${response}" | jq ".error.message")
|
|
[[ $? == 0 ]] && fail "Unable to add user: ${reason}"
|
|
fi
|
|
|
|
updatePassword $identityId
|
|
}
|
|
|
|
function updateStatus() {
|
|
email=$1
|
|
status=$2
|
|
|
|
identityId=$(findIdByEmail "$email")
|
|
[[ ${identityId} == "" ]] && fail "User not found"
|
|
|
|
response=$(curl -Ss "${kratosUrl}/identities/$identityId")
|
|
[[ $? != 0 ]] && fail "Unable to communicate with Kratos"
|
|
|
|
oldConfig=$(echo "select config from identity_credentials where identity_id=${identityId};" | sqlite3 "$databasePath")
|
|
if [[ "$status" == "locked" ]]; then
|
|
config=$(echo $oldConfig | sed -e 's/hashed/locked/')
|
|
echo "update identity_credentials set config=CAST('${config}' as BLOB) where identity_id=${identityId};" | sqlite3 "$databasePath"
|
|
[[ $? != 0 ]] && fail "Unable to lock credential record"
|
|
|
|
echo "delete from sessions where identity_id=${identityId};" | sqlite3 "$databasePath"
|
|
[[ $? != 0 ]] && fail "Unable to invalidate sessions"
|
|
else
|
|
config=$(echo $oldConfig | sed -e 's/locked/hashed/')
|
|
echo "update identity_credentials set config=CAST('${config}' as BLOB) where identity_id=${identityId};" | sqlite3 "$databasePath"
|
|
[[ $? != 0 ]] && fail "Unable to unlock credential record"
|
|
fi
|
|
|
|
updatedJson=$(echo "$response" | jq ".traits.status = \"$status\" | del(.verifiable_addresses) | del(.id) | del(.schema_url)")
|
|
response=$(curl -Ss -XPUT ${kratosUrl}/identities/$identityId -d "$updatedJson")
|
|
[[ $? != 0 ]] && fail "Unable to mark user as locked"
|
|
|
|
}
|
|
|
|
function updateUser() {
|
|
email=$1
|
|
|
|
identityId=$(findIdByEmail "$email")
|
|
[[ ${identityId} == "" ]] && fail "User not found"
|
|
|
|
updatePassword $identityId
|
|
}
|
|
|
|
function deleteUser() {
|
|
email=$1
|
|
|
|
identityId=$(findIdByEmail "$email")
|
|
[[ ${identityId} == "" ]] && fail "User not found"
|
|
|
|
response=$(curl -Ss -XDELETE "${kratosUrl}/identities/$identityId")
|
|
[[ $? != 0 ]] && fail "Unable to communicate with Kratos"
|
|
}
|
|
|
|
case "${operation}" in
|
|
"add")
|
|
verifyEnvironment
|
|
[[ "$email" == "" ]] && fail "Email address must be provided"
|
|
|
|
validateEmail "$email"
|
|
updatePassword
|
|
createUser "$email"
|
|
echo "Successfully added new user to SOC"
|
|
check_container thehive && echo $password | so-thehive-user-add "$email"
|
|
check_container fleet && echo $password | so-fleet-user-add "$email"
|
|
;;
|
|
|
|
"list")
|
|
verifyEnvironment
|
|
listUsers
|
|
;;
|
|
|
|
"update")
|
|
verifyEnvironment
|
|
[[ "$email" == "" ]] && fail "Email address must be provided"
|
|
|
|
updateUser "$email"
|
|
echo "Successfully updated user"
|
|
;;
|
|
|
|
"enable")
|
|
verifyEnvironment
|
|
[[ "$email" == "" ]] && fail "Email address must be provided"
|
|
|
|
updateStatus "$email" 'active'
|
|
echo "Successfully enabled user"
|
|
check_container thehive && so-thehive-user-enable "$email" true
|
|
check_container fleet && so-fleet-user-enable "$email" true
|
|
;;
|
|
|
|
"disable")
|
|
verifyEnvironment
|
|
[[ "$email" == "" ]] && fail "Email address must be provided"
|
|
|
|
updateStatus "$email" 'locked'
|
|
echo "Successfully disabled user"
|
|
check_container thehive && so-thehive-user-enable "$email" false
|
|
check_container fleet && so-fleet-user-enable "$email" false
|
|
;;
|
|
|
|
"delete")
|
|
verifyEnvironment
|
|
[[ "$email" == "" ]] && fail "Email address must be provided"
|
|
|
|
deleteUser "$email"
|
|
echo "Successfully deleted user"
|
|
check_container thehive && so-thehive-user-enable "$email" false
|
|
check_container fleet && so-fleet-user-enable "$email" false
|
|
;;
|
|
|
|
"validate")
|
|
validateEmail "$email"
|
|
updatePassword
|
|
echo "Email and password are acceptable"
|
|
;;
|
|
|
|
"valemail")
|
|
validateEmail "$email"
|
|
echo "Email is acceptable"
|
|
;;
|
|
|
|
"valpass")
|
|
updatePassword
|
|
echo "Password is acceptable"
|
|
;;
|
|
|
|
*)
|
|
fail "Unsupported operation: $operation"
|
|
;;
|
|
esac
|
|
|
|
exit 0 |