mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2025-12-06 09:12:45 +01:00
407 lines
9.8 KiB
Bash
Executable File
407 lines
9.8 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
|
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
|
# https://securityonion.net/license; you may not use this file except in compliance with the
|
|
# Elastic License 2.0.
|
|
|
|
if [[ -f /usr/sbin/so-common ]]; then
|
|
source /usr/sbin/so-common
|
|
else
|
|
source $(dirname $0)/../../../common/tools/sbin/so-common
|
|
fi
|
|
|
|
function usage() {
|
|
cat <<USAGE_EOF
|
|
Usage: $0 <operation> [supporting parameters]
|
|
|
|
where <operation> is one of the following:
|
|
|
|
list: Lists all client IDs and permissions currently defined in the oauth2 system
|
|
|
|
add: Adds a new client to the oauth2 system and outputs the generated secret
|
|
Required parameters:
|
|
--name <name>
|
|
Optional parameters:
|
|
--note <note> (defaults to blank)
|
|
--json output as JSON
|
|
|
|
delete: Deletes a client from the oauth2 system
|
|
Required parameters:
|
|
--id <id>
|
|
|
|
addperm: Grants a permission to an existing client
|
|
Required parameters:
|
|
--id <id>
|
|
--permission <permission>
|
|
|
|
delperm: Removes a permission from an existing client
|
|
Required parameters:
|
|
--id <id>
|
|
--permission <permission>
|
|
|
|
update: Updates a client name and note.
|
|
Required parameters:
|
|
--id <id>
|
|
--name <name>
|
|
--note <note>
|
|
--searchusername <run-as username>
|
|
|
|
generate-secret: Regenerates a client's secret and outputs the new secret.
|
|
Required parameters:
|
|
--id <id>
|
|
Optional parameters:
|
|
--json output as JSON
|
|
|
|
USAGE_EOF
|
|
exit 1
|
|
}
|
|
|
|
if [[ $# -lt 1 || $1 == --help || $1 == -h || $1 == -? || $1 == --h ]]; then
|
|
usage
|
|
fi
|
|
|
|
operation=$1
|
|
shift
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
param=$1
|
|
shift
|
|
case "$param" in
|
|
--id)
|
|
id=$1
|
|
[[ ${#id} -gt 55 ]] && fail "id cannot be longer than 55 characters"
|
|
shift
|
|
;;
|
|
--permission)
|
|
perm=$1
|
|
[[ ${#perm} -gt 50 ]] && fail "permission cannot be longer than 50 characters"
|
|
shift
|
|
;;
|
|
--name)
|
|
name=$1
|
|
[[ ${#name} -gt 50 ]] && fail "name cannot be longer than 50 characters"
|
|
shift
|
|
;;
|
|
--note)
|
|
note=$1
|
|
[[ ${#note} -gt 100 ]] && fail "note cannot be longer than 100 characters"
|
|
shift
|
|
;;
|
|
--searchusername)
|
|
searchusername=$1
|
|
[[ ${#searchusername} -gt 50 ]] && fail "search username cannot be longer than 50 characters"
|
|
shift
|
|
;;
|
|
--json)
|
|
json=1
|
|
;;
|
|
*)
|
|
echo "Encountered unexpected parameter: $param"
|
|
usage
|
|
;;
|
|
esac
|
|
done
|
|
|
|
hydraUrl=${HYDRA_URL:-http://127.0.0.1:4445}
|
|
socRolesFile=${SOC_ROLES_FILE:-/opt/so/conf/soc/soc_clients_roles}
|
|
soUID=${SOCORE_UID:-939}
|
|
soGID=${SOCORE_GID:-939}
|
|
|
|
function lock() {
|
|
# Obtain file descriptor lock
|
|
exec 99>/var/tmp/so-client.lock || fail "Unable to create lock descriptor; if the system was not shutdown gracefully you may need to remove /var/tmp/so-client.lock manually."
|
|
flock -w 10 99 || fail "Another process is using so-client; if the system was not shutdown gracefully you may need to remove /var/tmp/so-client.lock manually."
|
|
trap 'rm -f /var/tmp/so-client.lock' EXIT
|
|
}
|
|
|
|
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 "jq"
|
|
require "curl"
|
|
response=$(curl -Ss -L ${hydraUrl}/)
|
|
[[ "$response" != *"Error 404"* ]] && fail "Unable to communicate with Hydra; specify URL via HYDRA_URL environment variable"
|
|
}
|
|
|
|
function createFile() {
|
|
filename=$1
|
|
uid=$2
|
|
gid=$3
|
|
|
|
mkdir -p $(dirname "$filename")
|
|
truncate -s 0 "$filename"
|
|
chmod 600 "$filename"
|
|
chown "${uid}:${gid}" "$filename"
|
|
}
|
|
|
|
function ensureRoleFileExists() {
|
|
if [[ ! -f "$socRolesFile" || ! -s "$socRolesFile" ]]; then
|
|
# Generate the new roles file
|
|
rolesTmpFile="${socRolesFile}.tmp"
|
|
createFile "$rolesTmpFile" "$soUID" "$soGID"
|
|
|
|
if [[ -d "$socRolesFile" ]]; then
|
|
echo "Removing invalid roles directory created by Docker"
|
|
rm -fr "$socRolesFile"
|
|
fi
|
|
mv "${rolesTmpFile}" "${socRolesFile}"
|
|
fi
|
|
}
|
|
|
|
function listClients() {
|
|
response=$(curl -Ss -L -f ${hydraUrl}/admin/clients)
|
|
[[ $? != 0 ]] && fail "Unable to communicate with Hydra"
|
|
|
|
clientIds=$(echo "${response}" | jq -r ".[] | .client_id" | sort)
|
|
for clientId in $clientIds; do
|
|
perms=$(grep ":$clientId\$" "$socRolesFile" | cut -d: -f1 | tr '\n' ' ')
|
|
echo "$clientId: $perms"
|
|
done
|
|
}
|
|
|
|
function addClientPermission() {
|
|
id=$1
|
|
perm=$2
|
|
|
|
adjustClientPermission "$id" "$perm" "add"
|
|
}
|
|
|
|
function deleteClientPermission() {
|
|
id=$1
|
|
perm=$2
|
|
|
|
adjustClientPermission "$id" "$perm" "del"
|
|
}
|
|
|
|
function adjustClientPermission() {
|
|
identityId=$1
|
|
perm=$2
|
|
op=$3
|
|
|
|
[[ ${identityId} == "" ]] && fail "Client not found"
|
|
|
|
ensureRoleFileExists
|
|
|
|
filename="$socRolesFile"
|
|
hasPerm=0
|
|
grep "^$perm:" "$socRolesFile" | grep -q "$identityId" && hasPerm=1
|
|
if [[ "$op" == "add" ]]; then
|
|
if [[ "$hasPerm" == "1" ]]; then
|
|
echo "Client '$identityId' already has the permission: $perm"
|
|
return 1
|
|
else
|
|
echo "$perm:$identityId" >> "$filename"
|
|
fi
|
|
elif [[ "$op" == "del" ]]; then
|
|
if [[ "$hasPerm" -ne 1 ]]; then
|
|
fail "Client '$identityId' does not have the permission: $perm"
|
|
else
|
|
sed -e "\!^$perm:$identityId\$!d" "$filename" > "$filename.tmp"
|
|
cat "$filename".tmp > "$filename"
|
|
rm -f "$filename".tmp
|
|
fi
|
|
else
|
|
fail "Unsupported permission adjustment operation: $op"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
function convertNameToId() {
|
|
name=$1
|
|
|
|
name=${name//[^[:alnum:]]/_}
|
|
echo "socl_$name" | tr '[:upper:]' '[:lower:]'
|
|
}
|
|
|
|
function createClient() {
|
|
name=$1
|
|
perm=$2
|
|
note=$3
|
|
|
|
id=$(convertNameToId "$name")
|
|
now=$(date -u +%FT%TZ)
|
|
secret=$(get_random_value)
|
|
body=$(cat <<EOF
|
|
{
|
|
"access_token_strategy": "opaque",
|
|
"client_id": "$id",
|
|
"client_secret": "$secret",
|
|
"client_name": "$name",
|
|
"grant_types": [ "client_credentials" ],
|
|
"response_types": [ "code" ],
|
|
"metadata": {
|
|
"note": "$note"
|
|
}
|
|
}
|
|
EOF
|
|
)
|
|
|
|
response=$(curl -Ss -L --fail-with-body -X POST ${hydraUrl}/admin/clients -d "$body")
|
|
if [[ $? != 0 ]]; then
|
|
error=$(echo $response | jq .error)
|
|
fail "Failed to submit request to Hydra: $error"
|
|
fi
|
|
}
|
|
|
|
function update() {
|
|
clientId=$1
|
|
name=$2
|
|
note=$3
|
|
|
|
body=$(cat <<EOF
|
|
[
|
|
{
|
|
"op": "replace",
|
|
"path": "/client_name",
|
|
"value": "$name"
|
|
},
|
|
{
|
|
"op": "replace",
|
|
"path": "/metadata",
|
|
"value": {
|
|
"note": "$note"
|
|
}
|
|
}
|
|
]
|
|
EOF
|
|
)
|
|
|
|
response=$(curl -Ss -L --fail-with-body -X PATCH ${hydraUrl}/admin/clients/$id -d "$body")
|
|
if [[ $? != 0 ]]; then
|
|
error=$(echo $response | jq .error)
|
|
fail "Failed to submit request to Hydra: $error"
|
|
fi
|
|
}
|
|
|
|
function generateSecret() {
|
|
clientId=$1
|
|
|
|
secret=$(get_random_value)
|
|
body=$(cat <<EOF
|
|
[
|
|
{
|
|
"op": "replace",
|
|
"path": "/client_secret",
|
|
"value": "$secret"
|
|
}
|
|
]
|
|
EOF
|
|
)
|
|
|
|
response=$(curl -Ss -L --fail-with-body -X PATCH ${hydraUrl}/admin/clients/$id -d "$body")
|
|
if [[ $? != 0 ]]; then
|
|
error=$(echo $response | jq .error)
|
|
fail "Failed to submit request to Hydra: $error"
|
|
fi
|
|
}
|
|
|
|
function deleteClient() {
|
|
identityId=$1
|
|
|
|
[[ ${identityId} == "" ]] && fail "Client not found"
|
|
|
|
response=$(curl -Ss -XDELETE -L --fail-with-body "${hydraUrl}/admin/clients/$identityId")
|
|
if [[ $? != 0 ]]; then
|
|
error=$(echo $response | jq .error)
|
|
fail "Failed to submit request to Hydra: $error"
|
|
fi
|
|
|
|
rolesTmpFile="${socRolesFile}.tmp"
|
|
createFile "$rolesTmpFile" "$soUID" "$soGID"
|
|
grep -v "$identityId" "$socRolesFile" > "$rolesTmpFile"
|
|
cat "$rolesTmpFile" > "$socRolesFile"
|
|
}
|
|
|
|
case "${operation}" in
|
|
"add")
|
|
verifyEnvironment
|
|
[[ "$name" == "" ]] && fail "A short client name must be provided"
|
|
|
|
lock
|
|
createClient "$name" "${note}"
|
|
if [[ "$json" == "1" ]]; then
|
|
echo "{\"id\":\"$id\",\"secret\":\"$secret\"}"
|
|
else
|
|
echo "Successfully added client ID $id with generated secret: $secret"
|
|
fi
|
|
;;
|
|
|
|
"list")
|
|
verifyEnvironment
|
|
listClients
|
|
;;
|
|
|
|
"addperm")
|
|
verifyEnvironment
|
|
[[ "$id" == "" ]] && fail "Id must be provided"
|
|
[[ "$perm" == "" ]] && fail "Permission must be provided"
|
|
|
|
lock
|
|
if addClientPermission "$id" "$perm"; then
|
|
echo "Successfully added permission to client"
|
|
fi
|
|
;;
|
|
|
|
"delperm")
|
|
verifyEnvironment
|
|
[[ "$id" == "" ]] && fail "Id must be provided"
|
|
[[ "$perm" == "" ]] && fail "Permission must be provided"
|
|
|
|
lock
|
|
deleteClientPermission "$id" "$perm"
|
|
echo "Successfully removed permission from client"
|
|
;;
|
|
|
|
"update")
|
|
verifyEnvironment
|
|
[[ "$id" == "" ]] && fail "Id must be provided"
|
|
[[ "$name" == "" ]] && fail "Name must be provided"
|
|
[[ "$note" == "" ]] && fail "Note must be provided"
|
|
[[ "$searchusername" == "" ]] && fail "Search Username must be provided"
|
|
|
|
lock
|
|
update "$id" "$name" "$note" "$searchusername"
|
|
echo "Successfully updated client"
|
|
;;
|
|
|
|
"generate-secret")
|
|
verifyEnvironment
|
|
[[ "$id" == "" ]] && fail "Id must be provided"
|
|
|
|
lock
|
|
generateSecret "$id"
|
|
if [[ "$json" == "1" ]]; then
|
|
echo "{\"secret\":\"$secret\"}"
|
|
else
|
|
echo "Successfully generated secret: $secret"
|
|
fi
|
|
;;
|
|
|
|
"delete")
|
|
verifyEnvironment
|
|
[[ "$id" == "" ]] && fail "Id must be provided"
|
|
|
|
lock
|
|
deleteClient "$id"
|
|
echo "Successfully deleted client."
|
|
;;
|
|
*)
|
|
fail "Unsupported operation: $operation"
|
|
usage
|
|
;;
|
|
esac
|
|
|
|
exit 0
|