mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2025-12-06 17:22:49 +01:00
Use user ID instead of email as role master
This commit is contained in:
@@ -49,8 +49,11 @@ databasePath=${KRATOS_DB_PATH:-/opt/so/conf/kratos/db/db.sqlite}
|
|||||||
bcryptRounds=${BCRYPT_ROUNDS:-12}
|
bcryptRounds=${BCRYPT_ROUNDS:-12}
|
||||||
elasticUsersFile=${ELASTIC_USERS_FILE:-/opt/so/saltstack/local/salt/elasticsearch/files/users}
|
elasticUsersFile=${ELASTIC_USERS_FILE:-/opt/so/saltstack/local/salt/elasticsearch/files/users}
|
||||||
elasticRolesFile=${ELASTIC_ROLES_FILE:-/opt/so/saltstack/local/salt/elasticsearch/files/users_roles}
|
elasticRolesFile=${ELASTIC_ROLES_FILE:-/opt/so/saltstack/local/salt/elasticsearch/files/users_roles}
|
||||||
|
socRolesFile=${SOC_ROLES_FILE:-/opt/so/conf/soc/soc_users_roles}
|
||||||
esUID=${ELASTIC_UID:-930}
|
esUID=${ELASTIC_UID:-930}
|
||||||
esGID=${ELASTIC_GID:-930}
|
esGID=${ELASTIC_GID:-930}
|
||||||
|
soUID=${SOCORE_UID:-939}
|
||||||
|
soGID=${SOCORE_GID:-939}
|
||||||
|
|
||||||
function lock() {
|
function lock() {
|
||||||
# Obtain file descriptor lock
|
# Obtain file descriptor lock
|
||||||
@@ -87,7 +90,7 @@ function findIdByEmail() {
|
|||||||
email=$1
|
email=$1
|
||||||
|
|
||||||
response=$(curl -Ss -L ${kratosUrl}/identities)
|
response=$(curl -Ss -L ${kratosUrl}/identities)
|
||||||
identityId=$(echo "${response}" | jq ".[] | select(.verifiable_addresses[0].value == \"$email\") | .id")
|
identityId=$(echo "${response}" | jq -r ".[] | select(.verifiable_addresses[0].value == \"$email\") | .id")
|
||||||
echo $identityId
|
echo $identityId
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,32 +142,39 @@ function updatePassword() {
|
|||||||
# Generate password hash
|
# Generate password hash
|
||||||
passwordHash=$(hashPassword "$password")
|
passwordHash=$(hashPassword "$password")
|
||||||
# Update DB with new hash
|
# Update DB with new hash
|
||||||
echo "update identity_credentials set config=CAST('{\"hashed_password\":\"$passwordHash\"}' as BLOB) where identity_id=${identityId};" | sqlite3 "$databasePath"
|
echo "update identity_credentials set config=CAST('{\"hashed_password\":\"$passwordHash\"}' as BLOB) where identity_id='${identityId}';" | sqlite3 "$databasePath"
|
||||||
[[ $? != 0 ]] && fail "Unable to update password"
|
[[ $? != 0 ]] && fail "Unable to update password"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function createElasticFile() {
|
function createFile() {
|
||||||
filename=$1
|
filename=$1
|
||||||
|
uid=$2
|
||||||
|
gid=$3
|
||||||
|
|
||||||
truncate -s 0 "$filename"
|
truncate -s 0 "$filename"
|
||||||
chmod 600 "$filename"
|
chmod 600 "$filename"
|
||||||
chown "${esUID}:${esGID}" "$filename"
|
chown "${uid}:${gid}" "$filename"
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureRoleFileExists() {
|
function ensureRoleFileExists() {
|
||||||
if [ ! -f "$elasticRolesFile" ]; then
|
if [ ! -s "$socRolesFile" ]; then
|
||||||
echo "Creating new roles file: $elasticRolesFile"
|
echo "Migrating roles to new file: $socRolesFile"
|
||||||
rolesTmpFile="${elasticRolesFile}.tmp"
|
|
||||||
createElasticFile "${rolesTmpFile}"
|
rolesTmpFile="${socRolesFile}.tmp"
|
||||||
authPillarJson=$(lookup_salt_value "auth" "elasticsearch" "pillar" "json")
|
createFile "$rolesTmpFile" "$soUID" "$soGID"
|
||||||
syncElasticSystemRole "$authPillarJson" "so_elastic_user" "superuser" "$rolesTmpFile"
|
|
||||||
syncElasticSystemRole "$authPillarJson" "so_kibana_user" "superuser" "$rolesTmpFile"
|
if [[ -f "$databasePath" ]]; then
|
||||||
syncElasticSystemRole "$authPillarJson" "so_logstash_user" "superuser" "$rolesTmpFile"
|
# Generate the new users file
|
||||||
syncElasticSystemRole "$authPillarJson" "so_beats_user" "superuser" "$rolesTmpFile"
|
echo "select 'superuser:' || id from identities;" | sqlite3 "$databasePath" \
|
||||||
syncElasticSystemRole "$authPillarJson" "so_monitor_user" "remote_monitoring_collector" "$rolesTmpFile"
|
>> "$rolesTmpFile"
|
||||||
syncElasticSystemRole "$authPillarJson" "so_monitor_user" "remote_monitoring_agent" "$rolesTmpFile"
|
[[ $? != 0 ]] && fail "Unable to read identities from database"
|
||||||
syncElasticSystemRole "$authPillarJson" "so_monitor_user" "monitoring_user" "$rolesTmpFile"
|
else
|
||||||
mv "${rolesTmpFile}" "${elasticRolesFile}"
|
echo "Database file does not exist yet, installation is likely not yet complete."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mv "${rolesTmpFile}" "${socRolesFile}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,11 +206,12 @@ function syncElasticSystemRole() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function syncElastic() {
|
function syncElastic() {
|
||||||
echo "Syncing users between SOC and Elastic..."
|
echo "Syncing users and roles between SOC and Elastic..."
|
||||||
ensureRoleFileExists
|
|
||||||
|
|
||||||
usersTmpFile="${elasticUsersFile}.tmp"
|
usersTmpFile="${elasticUsersFile}.tmp"
|
||||||
createElasticFile "${usersTmpFile}"
|
createFile "${usersTmpFile}" "$esUID" "$esGID"
|
||||||
|
rolesTmpFile="${elasticRolesFile}.tmp"
|
||||||
|
createFile "${rolesTmpFile}" "$esUID" "$esGID"
|
||||||
|
|
||||||
authPillarJson=$(lookup_salt_value "auth" "elasticsearch" "pillar" "json")
|
authPillarJson=$(lookup_salt_value "auth" "elasticsearch" "pillar" "json")
|
||||||
|
|
||||||
@@ -210,8 +221,16 @@ function syncElastic() {
|
|||||||
syncElasticSystemUser "$authPillarJson" "so_beats_user" "$usersTmpFile"
|
syncElasticSystemUser "$authPillarJson" "so_beats_user" "$usersTmpFile"
|
||||||
syncElasticSystemUser "$authPillarJson" "so_monitor_user" "$usersTmpFile"
|
syncElasticSystemUser "$authPillarJson" "so_monitor_user" "$usersTmpFile"
|
||||||
|
|
||||||
if [[ -f "$databasePath" ]]; then
|
syncElasticSystemRole "$authPillarJson" "so_elastic_user" "superuser" "$rolesTmpFile"
|
||||||
# Generate the new users file
|
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"
|
||||||
|
|
||||||
|
if [[ -f "$databasePath" && -f "$socRolesFile" ]]; then
|
||||||
|
# Append the SOC users
|
||||||
echo "select '{\"user\":\"' || ici.identifier || '\", \"data\":' || ic.config || '}'" \
|
echo "select '{\"user\":\"' || ici.identifier || '\", \"data\":' || ic.config || '}'" \
|
||||||
"from identity_credential_identifiers ici, identity_credentials ic " \
|
"from identity_credential_identifiers ici, identity_credentials ic " \
|
||||||
"where ici.identity_credential_id=ic.id and instr(ic.config, 'hashed_password') " \
|
"where ici.identity_credential_id=ic.id and instr(ic.config, 'hashed_password') " \
|
||||||
@@ -220,12 +239,24 @@ function syncElastic() {
|
|||||||
jq -r '.user + ":" + .data.hashed_password' \
|
jq -r '.user + ":" + .data.hashed_password' \
|
||||||
>> "$usersTmpFile"
|
>> "$usersTmpFile"
|
||||||
[[ $? != 0 ]] && fail "Unable to read credential hashes from database"
|
[[ $? != 0 ]] && fail "Unable to read credential hashes from database"
|
||||||
|
|
||||||
|
# Append the user roles
|
||||||
|
while IFS="" read -r rolePair || [ -n "$rolePair" ]; do
|
||||||
|
userId=$(echo "$rolePair" | cut -d: -f2)
|
||||||
|
role=$(echo "$rolePair" | cut -d: -f1)
|
||||||
|
echo "select '$role:' || ici.identifier " \
|
||||||
|
"from identity_credential_identifiers ici, identity_credentials ic " \
|
||||||
|
"where ici.identity_credential_id=ic.id and ic.identity_id = '$userId';" | \
|
||||||
|
sqlite3 "$databasePath" >> "$rolesTmpFile"
|
||||||
|
done < "$socRolesFile"
|
||||||
|
|
||||||
else
|
else
|
||||||
echo "Database file does not exist yet, skipping users export"
|
echo "Database file or soc roles file does not exist yet, skipping users export"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -s "${usersTmpFile}" ]]; then
|
if [[ -s "${usersTmpFile}" ]]; then
|
||||||
mv "${usersTmpFile}" "${elasticUsersFile}"
|
mv "${usersTmpFile}" "${elasticUsersFile}"
|
||||||
|
mv "${rolesTmpFile}" "${elasticRolesFile}"
|
||||||
|
|
||||||
if [[ -z "$SKIP_STATE_APPLY" ]]; then
|
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 may take several minutes..."
|
||||||
@@ -238,15 +269,22 @@ function syncElastic() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function syncAll() {
|
function syncAll() {
|
||||||
|
ensureRoleFileExists
|
||||||
|
|
||||||
|
# Check if a sync is needed. Sync is not needed if the following are true:
|
||||||
|
# - user database entries are all older than the elastic users file
|
||||||
|
# - soc roles file last modify date is older than the elastic roles file
|
||||||
if [[ -z "$FORCE_SYNC" && -f "$databasePath" && -f "$elasticUsersFile" ]]; then
|
if [[ -z "$FORCE_SYNC" && -f "$databasePath" && -f "$elasticUsersFile" ]]; then
|
||||||
usersFileAgeSecs=$(echo $(($(date +%s) - $(date +%s -r "$elasticUsersFile"))))
|
usersFileAgeSecs=$(echo $(($(date +%s) - $(date +%s -r "$elasticUsersFile"))))
|
||||||
staleCount=$(echo "select count(*) from identity_credentials where updated_at >= Datetime('now', '-${usersFileAgeSecs} seconds');" \
|
staleCount=$(echo "select count(*) from identity_credentials where updated_at >= Datetime('now', '-${usersFileAgeSecs} seconds');" \
|
||||||
| sqlite3 "$databasePath")
|
| sqlite3 "$databasePath")
|
||||||
if [[ "$staleCount" == "0" ]]; then
|
if [[ "$staleCount" == "0" && "$elasticRolesFile" -nt "$socRolesFile" ]]; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
syncElastic
|
syncElastic
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,20 +323,20 @@ function adjustUserRole() {
|
|||||||
|
|
||||||
ensureRoleFileExists
|
ensureRoleFileExists
|
||||||
|
|
||||||
filename="$elasticRolesFile"
|
filename="$socRolesFile"
|
||||||
hasRole=0
|
hasRole=0
|
||||||
grep "$role:" "$elasticRolesFile" | grep -q "$email" && hasRole=1
|
grep "$role:" "$socRolesFile" | grep -q "$identityId" && hasRole=1
|
||||||
if [[ "$op" == "add" ]]; then
|
if [[ "$op" == "add" ]]; then
|
||||||
if [[ "$hasRole" == "1" ]]; then
|
if [[ "$hasRole" == "1" ]]; then
|
||||||
fail "User '$email' already has the role: $role"
|
fail "User '$email' already has the role: $role"
|
||||||
else
|
else
|
||||||
echo "$role:$email" >> "$filename"
|
echo "$role:$identityId" >> "$filename"
|
||||||
fi
|
fi
|
||||||
elif [[ "$op" == "del" ]]; then
|
elif [[ "$op" == "del" ]]; then
|
||||||
if [[ "$hasRole" -ne 1 ]]; then
|
if [[ "$hasRole" -ne 1 ]]; then
|
||||||
fail "User '$email' does not have the role: $role"
|
fail "User '$email' does not have the role: $role"
|
||||||
else
|
else
|
||||||
sed -i "/^$role:$email\$/d" "$filename"
|
sed -i "/^$role:$identityId\$/d" "$filename"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
fail "Unsupported role adjustment operation: $op"
|
fail "Unsupported role adjustment operation: $op"
|
||||||
@@ -321,7 +359,7 @@ EOF
|
|||||||
response=$(curl -Ss -L ${kratosUrl}/identities -d "$addUserJson")
|
response=$(curl -Ss -L ${kratosUrl}/identities -d "$addUserJson")
|
||||||
[[ $? != 0 ]] && fail "Unable to communicate with Kratos"
|
[[ $? != 0 ]] && fail "Unable to communicate with Kratos"
|
||||||
|
|
||||||
identityId=$(echo "${response}" | jq ".id")
|
identityId=$(echo "${response}" | jq -r ".id")
|
||||||
if [[ ${identityId} == "null" ]]; then
|
if [[ ${identityId} == "null" ]]; then
|
||||||
code=$(echo "${response}" | jq ".error.code")
|
code=$(echo "${response}" | jq ".error.code")
|
||||||
[[ "${code}" == "409" ]] && fail "User already exists"
|
[[ "${code}" == "409" ]] && fail "User already exists"
|
||||||
@@ -332,7 +370,7 @@ EOF
|
|||||||
addUserRole "$email" "$role"
|
addUserRole "$email" "$role"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
updatePassword $identityId
|
updatePassword "$identityId"
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateStatus() {
|
function updateStatus() {
|
||||||
@@ -382,6 +420,11 @@ function deleteUser() {
|
|||||||
|
|
||||||
response=$(curl -Ss -XDELETE -L "${kratosUrl}/identities/$identityId")
|
response=$(curl -Ss -XDELETE -L "${kratosUrl}/identities/$identityId")
|
||||||
[[ $? != 0 ]] && fail "Unable to communicate with Kratos"
|
[[ $? != 0 ]] && fail "Unable to communicate with Kratos"
|
||||||
|
|
||||||
|
rolesTmpFile="${socRolesFile}.tmp"
|
||||||
|
createFile "$rolesTmpFile" "$soUID" "$soGID"
|
||||||
|
grep -v "$id" "$socRolesFile" > "$rolesTmpFile"
|
||||||
|
mv "$rolesTmpFile" "$socRolesFile"
|
||||||
}
|
}
|
||||||
|
|
||||||
case "${operation}" in
|
case "${operation}" in
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ so-soc:
|
|||||||
- /opt/so/conf/soc/banner.md:/opt/sensoroni/html/login/banner.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.js:/opt/sensoroni/html/js/custom.js:ro
|
||||||
- /opt/so/conf/soc/custom_roles:/opt/sensoroni/rbac/custom_roles: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/conf/soc/soc_users_roles:/opt/sensoroni/rbac/users_roles:rw
|
||||||
- /opt/so/log/soc/:/opt/sensoroni/logs/:rw
|
- /opt/so/log/soc/:/opt/sensoroni/logs/:rw
|
||||||
{%- if salt['pillar.get']('nodestab', {}) %}
|
{%- if salt['pillar.get']('nodestab', {}) %}
|
||||||
- extra_hosts:
|
- extra_hosts:
|
||||||
|
|||||||
Reference in New Issue
Block a user