mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2025-12-10 11:12:51 +01:00
Move files out of common
This commit is contained in:
11
salt/manager/tools/sbin/so-allow
Executable file
11
salt/manager/tools/sbin/so-allow
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/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.
|
||||
|
||||
echo "Please use the Configuration section in SOC to allow hosts"
|
||||
echo ""
|
||||
echo "If you need command line options on adding hosts please run so-firewall"
|
||||
|
||||
15
salt/manager/tools/sbin/so-allow-view
Executable file
15
salt/manager/tools/sbin/so-allow-view
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/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.
|
||||
|
||||
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
echo ""
|
||||
echo "Hosts/Networks that have access to login to the Security Onion Console:"
|
||||
|
||||
so-firewall includedhosts analyst
|
||||
138
salt/manager/tools/sbin/so-deny
Executable file
138
salt/manager/tools/sbin/so-deny
Executable file
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# 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.
|
||||
|
||||
|
||||
|
||||
import ipaddress
|
||||
import textwrap
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import argparse
|
||||
import re
|
||||
from lxml import etree as ET
|
||||
from xml.dom import minidom
|
||||
|
||||
|
||||
LOCAL_SALT_DIR='/opt/so/saltstack/local'
|
||||
VALID_ROLES = {
|
||||
'a': { 'role': 'analyst','desc': 'Analyst - 80/tcp, 443/tcp' },
|
||||
'b': { 'role': 'beats_endpoint', 'desc': 'Logstash Beat - 5044/tcp' },
|
||||
'e': { 'role': 'elasticsearch_rest', 'desc': 'Elasticsearch REST API - 9200/tcp' },
|
||||
'f': { 'role': 'strelka_frontend', 'desc': 'Strelka frontend - 57314/tcp' },
|
||||
's': { 'role': 'syslog', 'desc': 'Syslog device - 514/tcp/udp' },
|
||||
}
|
||||
|
||||
|
||||
def validate_ip_cidr(ip_cidr: str) -> bool:
|
||||
try:
|
||||
ipaddress.ip_address(ip_cidr)
|
||||
except ValueError:
|
||||
try:
|
||||
ipaddress.ip_network(ip_cidr)
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def role_prompt() -> str:
|
||||
print()
|
||||
print('Choose the role for the IP or Range you would like to deny')
|
||||
print()
|
||||
for role in VALID_ROLES:
|
||||
print(f'[{role}] - {VALID_ROLES[role]["desc"]}')
|
||||
print()
|
||||
role = input('Please enter your selection: ')
|
||||
if role in VALID_ROLES.keys():
|
||||
return VALID_ROLES[role]['role']
|
||||
else:
|
||||
print(f'Invalid role \'{role}\', please try again.', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def ip_prompt() -> str:
|
||||
ip = input('Enter a single ip address or range to deny (ex: 10.10.10.10 or 10.10.0.0/16): ')
|
||||
if validate_ip_cidr(ip):
|
||||
return ip
|
||||
else:
|
||||
print(f'Invalid IP address or CIDR block \'{ip}\', please try again.', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def apply(role: str, ip: str) -> int:
|
||||
firewall_cmd = ['so-firewall', 'excludehost', role, ip]
|
||||
salt_cmd = ['salt-call', 'state.apply', '-l', 'quiet', 'firewall', 'queue=True']
|
||||
print(f'Removing {ip} from the {role} role. This can take a few seconds...')
|
||||
cmd = subprocess.run(firewall_cmd)
|
||||
if cmd.returncode == 0:
|
||||
cmd = subprocess.run(salt_cmd, stdout=subprocess.DEVNULL)
|
||||
else:
|
||||
return cmd.returncode
|
||||
|
||||
|
||||
def main():
|
||||
if os.geteuid() != 0:
|
||||
print('You must run this script as root', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
main_parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=textwrap.dedent(f'''\
|
||||
additional information:
|
||||
To use this script in interactive mode call it with no arguments
|
||||
'''
|
||||
))
|
||||
|
||||
group = main_parser.add_argument_group(title='roles')
|
||||
group.add_argument('-a', dest='roles', action='append_const', const=VALID_ROLES['a']['role'], help="Analyst - 80/tcp, 443/tcp")
|
||||
group.add_argument('-b', dest='roles', action='append_const', const=VALID_ROLES['b']['role'], help="Logstash Beat - 5044/tcp")
|
||||
group.add_argument('-e', dest='roles', action='append_const', const=VALID_ROLES['e']['role'], help="Elasticsearch REST API - 9200/tcp")
|
||||
group.add_argument('-f', dest='roles', action='append_const', const=VALID_ROLES['f']['role'], help="Strelka frontend - 57314/tcp")
|
||||
group.add_argument('-s', dest='roles', action='append_const', const=VALID_ROLES['s']['role'], help="Syslog device - 514/tcp/udp")
|
||||
|
||||
ip_g = main_parser.add_argument_group(title='allow')
|
||||
ip_g.add_argument('-i', help="IP or CIDR block to disallow connections from, requires at least one role argument", metavar='', dest='ip')
|
||||
|
||||
args = main_parser.parse_args(sys.argv[1:])
|
||||
|
||||
if args.roles is None:
|
||||
role = role_prompt()
|
||||
ip = ip_prompt()
|
||||
try:
|
||||
return_code = apply(role, ip)
|
||||
except Exception as e:
|
||||
print(f'Unexpected exception occurred: {e}', file=sys.stderr)
|
||||
return_code = e.errno
|
||||
sys.exit(return_code)
|
||||
elif args.roles is not None and args.ip is None:
|
||||
if os.environ.get('IP') is None:
|
||||
main_parser.print_help()
|
||||
sys.exit(1)
|
||||
else:
|
||||
args.ip = os.environ['IP']
|
||||
|
||||
if validate_ip_cidr(args.ip):
|
||||
try:
|
||||
for role in args.roles:
|
||||
return_code = apply(role, args.ip)
|
||||
if return_code > 0:
|
||||
break
|
||||
except Exception as e:
|
||||
print(f'Unexpected exception occurred: {e}', file=sys.stderr)
|
||||
return_code = e.errno
|
||||
else:
|
||||
print(f'Invalid IP address or CIDR block \'{args.ip}\', please try again.', file=sys.stderr)
|
||||
return_code = 1
|
||||
|
||||
sys.exit(return_code)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(1)
|
||||
14
salt/manager/tools/sbin/so-docker-refresh
Executable file
14
salt/manager/tools/sbin/so-docker-refresh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/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.
|
||||
|
||||
|
||||
|
||||
. /usr/sbin/so-common
|
||||
. /usr/sbin/so-image-common
|
||||
|
||||
require_manager
|
||||
update_docker_containers "refresh"
|
||||
144
salt/manager/tools/sbin/so-elastic-auth-password-reset
Executable file
144
salt/manager/tools/sbin/so-elastic-auth-password-reset
Executable file
@@ -0,0 +1,144 @@
|
||||
#!/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.
|
||||
|
||||
|
||||
source $(dirname $0)/so-common
|
||||
require_manager
|
||||
|
||||
user=$1
|
||||
elasticUsersFile=${ELASTIC_USERS_FILE:-/opt/so/saltstack/local/salt/elasticsearch/files/users}
|
||||
elasticAuthPillarFile=${ELASTIC_AUTH_PILLAR_FILE:-/opt/so/saltstack/local/pillar/elasticsearch/auth.sls}
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
echo "Usage: $0 <user>"
|
||||
echo ""
|
||||
echo " where <user> is one of the following:"
|
||||
echo ""
|
||||
echo " all: Reset the password for the so_elastic, so_kibana, so_logstash, so_beats, and so_monitor users"
|
||||
echo " so_elastic: Reset the password for the so_elastic user"
|
||||
echo " so_kibana: Reset the password for the so_kibana user"
|
||||
echo " so_logstash: Reset the password for the so_logstash user"
|
||||
echo " so_beats: Reset the password for the so_beats user"
|
||||
echo " so_monitor: Reset the password for the so_monitor user"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# function to create a lock so that the so-user sync cronjob can't run while this is running
|
||||
function lock() {
|
||||
# Obtain file descriptor lock
|
||||
exec 99>/var/tmp/so-user.lock || fail "Unable to create lock descriptor; if the system was not shutdown gracefully you may need to remove /var/tmp/so-user.lock manually."
|
||||
flock -w 10 99 || fail "Another process is using so-user; if the system was not shutdown gracefully you may need to remove /var/tmp/so-user.lock manually."
|
||||
trap 'rm -f /var/tmp/so-user.lock' EXIT
|
||||
}
|
||||
|
||||
function unlock() {
|
||||
rm -f /var/tmp/so-user.lock
|
||||
}
|
||||
|
||||
function fail() {
|
||||
msg=$1
|
||||
echo "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function removeSingleUserPass() {
|
||||
local user=$1
|
||||
sed -i '/user: '"${user}"'/{N;/pass: /d}' "${elasticAuthPillarFile}"
|
||||
}
|
||||
|
||||
function removeAllUserPass() {
|
||||
local userList=("so_elastic" "so_kibana" "so_logstash" "so_beats" "so_monitor")
|
||||
|
||||
for u in ${userList[@]}; do
|
||||
removeSingleUserPass "$u"
|
||||
done
|
||||
}
|
||||
|
||||
function removeElasticUsersFile() {
|
||||
rm -f "$elasticUsersFile"
|
||||
}
|
||||
|
||||
function createElasticAuthPillar() {
|
||||
salt-call state.apply elasticsearch.auth queue=True
|
||||
}
|
||||
|
||||
# this will disable highstate to prevent a highstate from starting while the script is running
|
||||
# will also disable salt.minion-state-apply-test allow so-salt-minion-check cronjob to restart salt-minion service incase
|
||||
function disableSaltStates() {
|
||||
printf "\nDisabling salt.minion-state-apply-test and highstate from running.\n\n"
|
||||
salt-call state.disable salt.minion-state-apply-test
|
||||
salt-call state.disable highstate
|
||||
}
|
||||
|
||||
function enableSaltStates() {
|
||||
printf "\nEnabling salt.minion-state-apply-test and highstate.\n\n"
|
||||
salt-call state.enable salt.minion-state-apply-test
|
||||
salt-call state.enable highstate
|
||||
}
|
||||
|
||||
function killAllSaltJobs() {
|
||||
printf "\nKilling all running salt jobs.\n\n"
|
||||
salt-call saltutil.kill_all_jobs
|
||||
}
|
||||
|
||||
function soUserSync() {
|
||||
# apply this state to update /opt/so/saltstack/local/salt/elasticsearch/curl.config on the manager
|
||||
salt-call state.sls_id elastic_curl_config_distributed manager queue=True
|
||||
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' saltutil.kill_all_jobs
|
||||
# apply this state to get the curl.config
|
||||
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.sls_id elastic_curl_config common queue=True
|
||||
$(dirname $0)/so-user sync
|
||||
printf "\nApplying logstash state to the appropriate nodes.\n\n"
|
||||
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 logstash queue=True
|
||||
printf "\nApplying kibana state to the appropriate nodes.\n\n"
|
||||
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' state.apply kibana queue=True
|
||||
printf "\nApplying curator state to the appropriate nodes.\n\n"
|
||||
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 curator queue=True
|
||||
}
|
||||
|
||||
function highstateManager() {
|
||||
killAllSaltJobs
|
||||
printf "\nRunning highstate on the manager to finalize password reset.\n\n"
|
||||
salt-call state.highstate -linfo queue=True
|
||||
}
|
||||
|
||||
case "${user}" in
|
||||
|
||||
so_elastic | so_kibana | so_logstash | so_beats | so_monitor)
|
||||
lock
|
||||
killAllSaltJobs
|
||||
disableSaltStates
|
||||
removeSingleUserPass "$user"
|
||||
createElasticAuthPillar
|
||||
removeElasticUsersFile
|
||||
unlock
|
||||
soUserSync
|
||||
enableSaltStates
|
||||
highstateManager
|
||||
;;
|
||||
|
||||
all)
|
||||
lock
|
||||
killAllSaltJobs
|
||||
disableSaltStates
|
||||
removeAllUserPass
|
||||
createElasticAuthPillar
|
||||
removeElasticUsersFile
|
||||
unlock
|
||||
soUserSync
|
||||
enableSaltStates
|
||||
highstateManager
|
||||
;;
|
||||
|
||||
*)
|
||||
fail "Unsupported user: $user"
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
exit 0
|
||||
104
salt/manager/tools/sbin/so-firewall
Executable file
104
salt/manager/tools/sbin/so-firewall
Executable file
@@ -0,0 +1,104 @@
|
||||
#!/usr/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.
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo "Usage: $0 --role=<ROLE> --ip=<IP ADDRESS> --apply=<true|false>"
|
||||
echo ""
|
||||
echo " Example: so-firewall --role=sensor --ip=192.168.254.100 --apply=true"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for i in "$@"; do
|
||||
case $i in
|
||||
-r=*|--role=*)
|
||||
ROLE="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
-i=*|--ip=*)
|
||||
IP="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
-a=*|--apply*)
|
||||
APPLY="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
-*|--*)
|
||||
echo "Unknown option $i"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
ROLE=${ROLE,,}
|
||||
APPLY=${APPLY,,}
|
||||
|
||||
function rolecall() {
|
||||
THEROLE=$1
|
||||
THEROLES="analyst analyst_workstations beats_endpoint beats_endpoint_ssl elastic_agent_endpoint elasticsearch_rest endgame eval fleet heavynodes idh manager managersearch receivers searchnodes sensors standalone strelka_frontend syslog"
|
||||
|
||||
for AROLE in $THEROLES; do
|
||||
if [ "$AROLE" = "$THEROLE" ]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# Make sure the required options are specified
|
||||
if [ -z "$ROLE" ]; then
|
||||
echo "Please specify a role with --role="
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$IP" ]; then
|
||||
echo "Please specify an IP address with --ip="
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Are we dealing with a role that this script supports?
|
||||
if rolecall "$ROLE"; then
|
||||
echo "$ROLE is a supported role"
|
||||
else
|
||||
echo "This is not a supported role"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Are we dealing with an IP?
|
||||
if verify_ip4 "$IP"; then
|
||||
echo "$IP is a valid IP or CIDR"
|
||||
else
|
||||
echo "$IP is not a valid IP or CIDR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local_salt_dir=/opt/so/saltstack/local/salt/firewall
|
||||
|
||||
# Let's see if the file exists and if it does, let's see if the IP exists.
|
||||
if [ -f "$local_salt_dir/hostgroups/$ROLE" ]; then
|
||||
if grep -q $IP "$local_salt_dir/hostgroups/$ROLE"; then
|
||||
echo "Host already exists"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# If you have reached this part of your quest then let's add the IP
|
||||
echo "Adding $IP to the $ROLE role"
|
||||
echo "$IP" >> $local_salt_dir/hostgroups/$ROLE
|
||||
|
||||
# Check to see if we are applying this right away.
|
||||
if [ "$APPLY" = "true" ]; then
|
||||
echo "Applying the firewall rules"
|
||||
salt-call state.apply firewall queue=True
|
||||
echo "Firewall rules have been applied... Review logs further if there were errors."
|
||||
echo ""
|
||||
else
|
||||
echo "Firewall rules will be applied next salt run"
|
||||
fi
|
||||
82
salt/manager/tools/sbin/so-firewall-minion
Executable file
82
salt/manager/tools/sbin/so-firewall-minion
Executable file
@@ -0,0 +1,82 @@
|
||||
#!/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.
|
||||
|
||||
|
||||
. /usr/sbin/so-common
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo "Usage: $0 --role=<ROLE> --ip=<IP ADDRESS>"
|
||||
echo ""
|
||||
echo " Example: so-firewall-minion --role=manager --ip=192.168.254.100"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for i in "$@"; do
|
||||
case $i in
|
||||
-r=*|--role=*)
|
||||
ROLE="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
-i=*|--ip=*)
|
||||
IP="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
-*|--*)
|
||||
echo "Unknown option $i"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
ROLE=${ROLE^^}
|
||||
|
||||
if [ -z "$ROLE" ]; then
|
||||
echo "Please specify a role with --role="
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$IP" ]; then
|
||||
echo "Please specify an IP address with --ip="
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$ROLE" in
|
||||
|
||||
'MANAGER')
|
||||
so-firewall --role=manager --ip="$IP"
|
||||
;;
|
||||
'EVAL' | 'MANAGERSEARCH' | 'STANDALONE' | 'IMPORT')
|
||||
so-firewall --role=manager --ip="$IP"
|
||||
so-firewall --role=sensors --ip="$IP"
|
||||
so-firewall --apply=true --role=searchnodes --ip="$IP"
|
||||
;;
|
||||
'FLEET' | 'SENSOR' | 'SEARCHNODE' | 'HEAVYNODE' | 'IDH' | 'RECEIVER')
|
||||
case "$ROLE" in
|
||||
'FLEET')
|
||||
so-firewall --apply=true --role=fleet --ip="$IP"
|
||||
;;
|
||||
'SENSOR')
|
||||
so-firewall --apply=true --role=sensors --ip="$IP"
|
||||
;;
|
||||
'SEARCHNODE')
|
||||
so-firewall --apply=true --role=searchnodes --ip="$IP"
|
||||
;;
|
||||
'HEAVYNODE')
|
||||
so-firewall --role=sensors --ip="$IP"
|
||||
so-firewall --apply=true --role=heavynodes --ip="$IP"
|
||||
;;
|
||||
'IDH')
|
||||
so-firewall --apply=true --role=sensors --ip="$IP"
|
||||
;;
|
||||
'RECEIVER')
|
||||
so-firewall --apply=true --role=receivers --ip="$IP"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
364
salt/manager/tools/sbin/so-minion
Executable file
364
salt/manager/tools/sbin/so-minion
Executable file
@@ -0,0 +1,364 @@
|
||||
#!/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
|
||||
. /usr/sbin/so-common
|
||||
fi
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "This script must be run using sudo!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo "Usage: $0 -o=<operation> -m=[id]"
|
||||
echo ""
|
||||
echo " where <operation> is one of the following:"
|
||||
echo ""
|
||||
echo " list: Lists all keys with hashes"
|
||||
echo " accept: Accepts a new key and adds the minion files"
|
||||
echo " delete: Removes the key and deletes the minion files"
|
||||
echo " reject: Rejects a key"
|
||||
echo " test: Perform minion test"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for i in "$@"; do
|
||||
case $i in
|
||||
-o=*|--operation=*)
|
||||
OPERATION="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
-m=*|--minionid=*)
|
||||
MINION_ID="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
-e=*|--esheap=*)
|
||||
ES_HEAP_SIZE="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
-n=*|--mgmtnic=*)
|
||||
MNIC="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
-d=*|--description=*)
|
||||
NODE_DESCRIPTION="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
-a=*|--monitor=*)
|
||||
INTERFACE="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
-i=*|--ip=*)
|
||||
MAINIP="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
-*|--*)
|
||||
echo "Unknown option $i"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
PILLARFILE=/opt/so/saltstack/local/pillar/minions/$MINION_ID.sls
|
||||
ADVPILLARFILE=/opt/so/saltstack/local/pillar/minions/adv_$MINION_ID.sls
|
||||
|
||||
function getinstallinfo() {
|
||||
# Pull from file
|
||||
INSTALLVARS=$(sudo salt "$MINION_ID" cp.get_file_str /opt/so/install.txt --out=newline_values_only)
|
||||
source <(echo $INSTALLVARS)
|
||||
}
|
||||
|
||||
function testminion() {
|
||||
# Always run on the host, since this is going to be the manager of a distributed grid, or an eval/standalone.
|
||||
# Distributed managers must run this in order for the sensor nodes to have access to the so-tcpreplay image.
|
||||
so-test
|
||||
result=$?
|
||||
|
||||
# If this so-minion script is not running on the given minion ID, run so-test remotely on the sensor as well
|
||||
local_id=$(lookup_grain id)
|
||||
if [[ ! "$local_id" =~ "${MINION_ID}_" ]]; then
|
||||
salt "$MINION_ID" cmd.run 'so-test'
|
||||
result=$?
|
||||
fi
|
||||
|
||||
exit $result
|
||||
}
|
||||
|
||||
function listminions() {
|
||||
salt-key list -F --out=json
|
||||
exit $?
|
||||
}
|
||||
|
||||
function rejectminion() {
|
||||
salt-key -y -r $MINION_ID
|
||||
exit $?
|
||||
}
|
||||
|
||||
function acceptminion() {
|
||||
salt-key -y -a $MINION_ID
|
||||
}
|
||||
|
||||
function deleteminion() {
|
||||
salt-key -y -d $MINION_ID
|
||||
}
|
||||
|
||||
function deleteminionfiles () {
|
||||
rm -f $PILLARFILE
|
||||
rm -f $ADVPILLARFILE
|
||||
}
|
||||
|
||||
# Create the minion file
|
||||
function create_minion_files() {
|
||||
mkdir -p /opt/so/saltstack/local/pillar/minions
|
||||
touch $ADVPILLARFILE
|
||||
if [ -f "$PILLARFILE" ]; then
|
||||
rm $PILLARFILE
|
||||
fi
|
||||
}
|
||||
|
||||
# Add Elastic settings to the minion file
|
||||
function add_elastic_to_minion() {
|
||||
printf '%s\n'\
|
||||
"elasticsearch:"\
|
||||
" esheap: '$ES_HEAP_SIZE'"\
|
||||
" " >> $PILLARFILE
|
||||
}
|
||||
|
||||
# Add Elastic Fleet Server settings to the minion file
|
||||
function add_fleet_to_minion() {
|
||||
|
||||
# Create ES Token for Fleet server (Curl to Kibana API)
|
||||
# TODO: Add error handling
|
||||
ESTOKEN=$(curl -K /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fleet/service_tokens" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' | jq -r .value)
|
||||
|
||||
# Write out settings to minion file
|
||||
printf '%s\n'\
|
||||
"elasticfleet:"\
|
||||
" server:"\
|
||||
" es_token: '$ESTOKEN'"\
|
||||
" " >> $PILLARFILE
|
||||
}
|
||||
|
||||
|
||||
# Add IDH Services info to the minion file
|
||||
function add_idh_to_minion() {
|
||||
printf '%s\n'\
|
||||
"idh:"\
|
||||
" restrict_management_ip: $IDH_MGTRESTRICT"\
|
||||
" services:" >> "$PILLARFILE"
|
||||
IFS=',' read -ra IDH_SERVICES_ARRAY <<< "$IDH_SERVICES"
|
||||
for service in ${IDH_SERVICES_ARRAY[@]}; do
|
||||
echo " - $service" | tr '[:upper:]' '[:lower:]' | tr -d '"' >> "$PILLARFILE"
|
||||
done
|
||||
}
|
||||
|
||||
function add_logstash_to_minion() {
|
||||
# Create the logstash advanced pillar
|
||||
printf '%s\n'\
|
||||
"logstash:"\
|
||||
" config:"\
|
||||
" pipeline_x_workers: $CPUCORES"\
|
||||
" settings:"\
|
||||
" lsheap: $LSHEAP"\
|
||||
" " >> $PILLARFILE
|
||||
}
|
||||
|
||||
# Analyst Workstation
|
||||
function add_analyst_to_minion() {
|
||||
printf '%s\n'\
|
||||
"host:"\
|
||||
" mainint: '$MNIC'"\
|
||||
"workstation:"\
|
||||
" gui:"\
|
||||
" enabled: true"\
|
||||
"sensoroni:"\
|
||||
" node_description: '${NODE_DESCRIPTION//\'/''}'" >> $PILLARFILE
|
||||
}
|
||||
|
||||
# Add basic host info to the minion file
|
||||
function add_host_to_minion() {
|
||||
printf '%s\n'\
|
||||
"host:"\
|
||||
" mainip: '$MAINIP'"\
|
||||
" mainint: '$MNIC'" >> $PILLARFILE
|
||||
}
|
||||
|
||||
# Add sensoroni specific information - Can we pull node_adrees from the host pillar?
|
||||
function add_sensoroni_to_minion() {
|
||||
|
||||
printf '%s\n'\
|
||||
"sensoroni:"\
|
||||
" node_description: '${NODE_DESCRIPTION//\'/''}'"\
|
||||
" " >> $PILLARFILE
|
||||
}
|
||||
|
||||
# Sensor settings for the minion pillar
|
||||
function add_sensor_to_minion() {
|
||||
echo "sensor:" >> $PILLARFILE
|
||||
echo " interface: '$INTERFACE'" >> $PILLARFILE
|
||||
echo " mtu: 9000" >> $PILLARFILE
|
||||
echo "zeek:" >> $PILLARFILE
|
||||
echo " config:" >> $PILLARFILE
|
||||
echo " node:" >> $PILLARFILE
|
||||
echo " lb_procs: '$CORECOUNT'" >> $PILLARFILE
|
||||
echo "suricata:" >> $PILLARFILE
|
||||
echo " config:" >> $PILLARFILE
|
||||
echo " af-packet:" >> $PILLARFILE
|
||||
echo " threads: '$CORECOUNT'" >> $PILLARFILE
|
||||
echo "pcap:" >> $PILLARFILE
|
||||
echo " enabled: True" >> $PILLARFILE
|
||||
}
|
||||
|
||||
function create_fleet_policy() {
|
||||
|
||||
JSON_STRING=$( jq -n \
|
||||
--arg NAME "FleetServer_$LSHOSTNAME" \
|
||||
--arg DESC "Fleet Server - $LSHOSTNAME" \
|
||||
'{"name": $NAME,"id":$NAME,"description":$DESC,"namespace":"default","monitoring_enabled":["logs"],"inactivity_timeout":1209600,"has_fleet_server":true}'
|
||||
)
|
||||
|
||||
# Create Fleet Sever Policy
|
||||
curl -K /opt/so/conf/elasticsearch/curl.config -L -X POST "localhost:5601/api/fleet/agent_policies" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"
|
||||
|
||||
JSON_STRING_UPDATE=$( jq -n \
|
||||
--arg NAME "FleetServer_$LSHOSTNAME" \
|
||||
--arg DESC "Fleet Server - $LSHOSTNAME" \
|
||||
'{"name":$NAME,"description":$DESC,"namespace":"default","monitoring_enabled":["logs"],"inactivity_timeout":1209600,"data_output_id":"so-manager_elasticsearch"}'
|
||||
)
|
||||
|
||||
# Update Fleet Policy - ES Output
|
||||
curl -K /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/agent_policies/FleetServer_$LSHOSTNAME" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING_UPDATE"
|
||||
}
|
||||
|
||||
function update_fleet_host_urls() {
|
||||
# Query for current Fleet Host URLs & append New Fleet Node Hostname & IP
|
||||
JSON_STRING=$(curl -K /opt/so/conf/elasticsearch/curl.config 'http://localhost:5601/api/fleet/fleet_server_hosts' | jq --arg HOSTNAME "https://$LSHOSTNAME:8220" --arg IP "https://$MAINIP:8220" -r '.items[].host_urls += [ $HOSTNAME, $IP ] | {"name":"Default","host_urls": .items[].host_urls,"is_default":true,"proxy_id":null}')
|
||||
|
||||
# Update Fleet Host URLs
|
||||
curl -K /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/fleet_server_hosts/fleet-default-fleet-server-host" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"
|
||||
}
|
||||
|
||||
function update_logstash_outputs() {
|
||||
# Query for current Logstash outputs & append New Fleet Node Hostname & IP
|
||||
JSON_STRING=$(curl -K /opt/so/conf/elasticsearch/curl.config 'http://localhost:5601/api/fleet/outputs/so-manager_logstash' | jq --arg HOSTNAME "$LSHOSTNAME:5055" --arg IP "$MAINIP:5055" -r '.item.hosts += [ $HOSTNAME, $IP ] | {"name":"grid-logstash","type":"logstash","hosts": .item.hosts,"is_default":true,"is_default_monitoring":true,"config_yaml":""}')
|
||||
|
||||
# Update Logstash Outputs
|
||||
curl -K /opt/so/conf/elasticsearch/curl.config -L -X PUT "localhost:5601/api/fleet/outputs/so-manager_logstash" -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d "$JSON_STRING"
|
||||
}
|
||||
|
||||
function updateMine() {
|
||||
salt "$MINION_ID" mine.send network.ip_addrs interface="$MNIC"
|
||||
}
|
||||
function apply_ES_state() {
|
||||
salt-call state.apply elasticsearch concurrent=True
|
||||
}
|
||||
function createEVAL() {
|
||||
add_elastic_to_minion
|
||||
add_logstash_to_minion
|
||||
add_sensor_to_minion
|
||||
}
|
||||
|
||||
function createFLEET() {
|
||||
add_fleet_to_minion
|
||||
add_logstash_to_minion
|
||||
create_fleet_policy
|
||||
update_fleet_host_urls
|
||||
update_logstash_outputs
|
||||
}
|
||||
|
||||
function createIDH() {
|
||||
add_idh_to_minion
|
||||
}
|
||||
|
||||
function createIMPORT() {
|
||||
add_elastic_to_minion
|
||||
add_logstash_to_minion
|
||||
add_sensor_to_minion
|
||||
}
|
||||
|
||||
function createHEAVYNODE() {
|
||||
add_elastic_to_minion
|
||||
add_logstash_to_minion
|
||||
add_sensor_to_minion
|
||||
}
|
||||
|
||||
function createMANAGER() {
|
||||
add_elastic_to_minion
|
||||
add_logstash_to_minion
|
||||
}
|
||||
|
||||
function createMANAGERSEARCH() {
|
||||
add_elastic_to_minion
|
||||
add_logstash_to_minion
|
||||
}
|
||||
|
||||
function createSENSOR() {
|
||||
add_sensor_to_minion
|
||||
}
|
||||
|
||||
function createSEARCHNODE() {
|
||||
add_elastic_to_minion
|
||||
add_logstash_to_minion
|
||||
updateMine
|
||||
apply_ES_state
|
||||
}
|
||||
|
||||
function createSTANDALONE() {
|
||||
add_elastic_to_minion
|
||||
add_logstash_to_minion
|
||||
add_sensor_to_minion
|
||||
}
|
||||
|
||||
function testConnection() {
|
||||
retry 15 3 "salt '$MINION_ID' test.ping" True
|
||||
local ret=$?
|
||||
if [[ $ret != 0 ]]; then
|
||||
echo "The Minion has been accepted but is not online. Try again later"
|
||||
echo "Deleting the key"
|
||||
deleteminion
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ "$OPERATION" = 'list' ]]; then
|
||||
listminions
|
||||
fi
|
||||
|
||||
if [[ "$OPERATION" = 'delete' ]]; then
|
||||
deleteminionfiles
|
||||
deleteminion
|
||||
fi
|
||||
|
||||
if [[ "$OPERATION" = 'add' || "$OPERATION" = 'setup' ]]; then
|
||||
# Skip this if its setup
|
||||
if [ $OPERATION != 'setup' ]; then
|
||||
# Accept the salt key
|
||||
acceptminion
|
||||
# Test to see if the minion was accepted
|
||||
testConnection
|
||||
# Pull the info from the file to build what is needed
|
||||
getinstallinfo
|
||||
fi
|
||||
# Check to see if nodetype is set
|
||||
if [ -z $NODETYPE ]; then
|
||||
echo "No node type specified"
|
||||
exit 1
|
||||
fi
|
||||
create_minion_files
|
||||
add_host_to_minion
|
||||
add_sensoroni_to_minion
|
||||
create$NODETYPE
|
||||
echo "Minion file created for $MINION_ID"
|
||||
fi
|
||||
|
||||
if [[ "$OPERATION" = 'test' ]]; then
|
||||
testminion
|
||||
fi
|
||||
16
salt/manager/tools/sbin/so-repo-sync
Normal file
16
salt/manager/tools/sbin/so-repo-sync
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/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.
|
||||
NOROOT=1
|
||||
. /usr/sbin/so-common
|
||||
|
||||
set_version
|
||||
set_os
|
||||
salt_minion_count
|
||||
|
||||
curl --retry 5 --retry-delay 60 -A "reposync/$VERSION/$OS/$(uname -r)/$MINIONCOUNT" https://sigs.securityonion.net/checkup --output /tmp/checkup
|
||||
dnf reposync --norepopath -g --delete -m -c /opt/so/conf/reposync/repodownload.conf --repoid=securityonionsync --download-metadata -p /nsm/repo/
|
||||
createrepo /nsm/repo
|
||||
55
salt/manager/tools/sbin/so-saltstack-update
Executable file
55
salt/manager/tools/sbin/so-saltstack-update
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/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.
|
||||
|
||||
|
||||
default_salt_dir=/opt/so/saltstack/default
|
||||
clone_to_tmp() {
|
||||
|
||||
# TODO Need to add a air gap option
|
||||
# Make a temp location for the files
|
||||
mkdir /tmp/sogh
|
||||
cd /tmp/sogh
|
||||
#git clone -b dev https://github.com/Security-Onion-Solutions/securityonion.git
|
||||
git clone https://github.com/Security-Onion-Solutions/securityonion.git
|
||||
cd /tmp
|
||||
|
||||
}
|
||||
|
||||
copy_new_files() {
|
||||
|
||||
# Copy new files over to the salt dir
|
||||
cd /tmp/sogh/securityonion
|
||||
git checkout $BRANCH
|
||||
VERSION=$(cat VERSION)
|
||||
# We need to overwrite if there is a repo file
|
||||
if [ -d /opt/so/repo ]; then
|
||||
tar -czf /opt/so/repo/"$VERSION".tar.gz -C "$(pwd)/.." .
|
||||
fi
|
||||
rsync -a salt $default_salt_dir/
|
||||
rsync -a pillar $default_salt_dir/
|
||||
chown -R socore:socore $default_salt_dir/salt
|
||||
chown -R socore:socore $default_salt_dir/pillar
|
||||
chmod 755 $default_salt_dir/pillar/firewall/addfirewall.sh
|
||||
|
||||
rm -rf /tmp/sogh
|
||||
}
|
||||
|
||||
got_root(){
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "This script must be run using sudo!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
got_root
|
||||
if [ $# -ne 1 ] ; then
|
||||
BRANCH=master
|
||||
else
|
||||
BRANCH=$1
|
||||
fi
|
||||
clone_to_tmp
|
||||
copy_new_files
|
||||
699
salt/manager/tools/sbin/so-user
Executable file
699
salt/manager/tools/sbin/so-user
Executable file
@@ -0,0 +1,699 @@
|
||||
#!/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.
|
||||
|
||||
|
||||
|
||||
source $(dirname $0)/so-common
|
||||
|
||||
DEFAULT_ROLE=analyst
|
||||
|
||||
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
|
||||
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:-/nsm/kratos/db/db.sqlite}
|
||||
databaseTimeout=${KRATOS_DB_TIMEOUT:-5000}
|
||||
bcryptRounds=${BCRYPT_ROUNDS:-12}
|
||||
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}
|
||||
socRolesFile=${SOC_ROLES_FILE:-/opt/so/conf/soc/soc_users_roles}
|
||||
esUID=${ELASTIC_UID:-930}
|
||||
esGID=${ELASTIC_GID:-930}
|
||||
soUID=${SOCORE_UID:-939}
|
||||
soGID=${SOCORE_GID:-939}
|
||||
|
||||
function lock() {
|
||||
# Obtain file descriptor lock
|
||||
exec 99>/var/tmp/so-user.lock || fail "Unable to create lock descriptor; if the system was not shutdown gracefully you may need to remove /var/tmp/so-user.lock manually."
|
||||
flock -w 10 99 || fail "Another process is using so-user; if the system was not shutdown gracefully you may need to remove /var/tmp/so-user.lock manually."
|
||||
trap 'rm -f /var/tmp/so-user.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 "htpasswd"
|
||||
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 -L ${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 -L ${kratosUrl}/identities)
|
||||
identityId=$(echo "${response}" | jq -r ".[] | select(.verifiable_addresses[0].value == \"$email\") | .id")
|
||||
echo $identityId
|
||||
}
|
||||
|
||||
function validatePassword() {
|
||||
password=$1
|
||||
|
||||
len=$(expr length "$password")
|
||||
if [[ $len -lt 8 ]]; then
|
||||
fail "Password does not meet the minimum requirements"
|
||||
fi
|
||||
if [[ $len -gt 72 ]]; then
|
||||
fail "Password is too long (max: 72)"
|
||||
fi
|
||||
check_password_and_exit "$password"
|
||||
}
|
||||
|
||||
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
|
||||
fail "Email address is invalid"
|
||||
fi
|
||||
|
||||
if [[ "$email" =~ [A-Z] ]]; then
|
||||
fail "Email addresses cannot contain uppercase letters"
|
||||
fi
|
||||
}
|
||||
|
||||
function hashPassword() {
|
||||
password=$1
|
||||
|
||||
passwordHash=$(echo "${password}" | htpasswd -niBC $bcryptRounds SOUSER)
|
||||
passwordHash=$(echo "$passwordHash" | cut -c 11-)
|
||||
passwordHash="\$2a${passwordHash}" # still waiting for https://github.com/elastic/elasticsearch/issues/51132
|
||||
echo "$passwordHash"
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
passwordHash=$(hashPassword "$password")
|
||||
# Update DB with new hash
|
||||
echo "update identity_credentials set config=CAST('{\"hashed_password\":\"$passwordHash\"}' as BLOB), created_at=datetime('now'), updated_at=datetime('now') where identity_id='${identityId}' and identity_credential_type_id=(select id from identity_credential_types where name='password');" | sqlite3 -cmd ".timeout ${databaseTimeout}" "$databasePath"
|
||||
# Deactivate MFA
|
||||
echo "delete from identity_credential_identifiers where identity_credential_id=(select id from identity_credentials where identity_id='${identityId}' and identity_credential_type_id=(select id from identity_credential_types where name='totp'));" | sqlite3 -cmd ".timeout ${databaseTimeout}" "$databasePath"
|
||||
echo "delete from identity_credentials where identity_id='${identityId}' and identity_credential_type_id=(select id from identity_credential_types where name='totp');" | sqlite3 -cmd ".timeout ${databaseTimeout}" "$databasePath"
|
||||
[[ $? != 0 ]] && fail "Unable to update password"
|
||||
fi
|
||||
}
|
||||
|
||||
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 users file
|
||||
rolesTmpFile="${socRolesFile}.tmp"
|
||||
createFile "$rolesTmpFile" "$soUID" "$soGID"
|
||||
|
||||
if [[ -f "$databasePath" ]]; then
|
||||
echo "Migrating roles to new file: $socRolesFile"
|
||||
|
||||
echo "select 'superuser:' || id from identities;" | sqlite3 -cmd ".timeout ${databaseTimeout}" "$databasePath" \
|
||||
>> "$rolesTmpFile"
|
||||
[[ $? != 0 ]] && fail "Unable to read identities from database"
|
||||
|
||||
echo "The following users have all been migrated with the super user role:"
|
||||
cat "${rolesTmpFile}"
|
||||
else
|
||||
echo "Database file does not exist yet, installation is likely not yet complete."
|
||||
fi
|
||||
|
||||
if [[ -d "$socRolesFile" ]]; then
|
||||
echo "Removing invalid roles directory created by Docker"
|
||||
rm -fr "$socRolesFile"
|
||||
fi
|
||||
mv "${rolesTmpFile}" "${socRolesFile}"
|
||||
fi
|
||||
}
|
||||
|
||||
function syncElasticSystemUser() {
|
||||
json=$1
|
||||
userid=$2
|
||||
usersFile=$3
|
||||
|
||||
user=$(echo "$json" | jq -r ".local.users.$userid.user")
|
||||
pass=$(echo "$json" | jq -r ".local.users.$userid.pass")
|
||||
|
||||
[[ -z "$user" || -z "$pass" ]] && fail "Elastic auth credentials for system user '$userid' are missing"
|
||||
hash=$(hashPassword "$pass")
|
||||
|
||||
echo "${user}:${hash}" >> "$usersFile"
|
||||
}
|
||||
|
||||
function syncElasticSystemRole() {
|
||||
json=$1
|
||||
userid=$2
|
||||
role=$3
|
||||
rolesFile=$4
|
||||
|
||||
user=$(echo "$json" | jq -r ".local.users.$userid.user")
|
||||
|
||||
[[ -z "$user" ]] && fail "Elastic auth credentials for system user '$userid' are missing"
|
||||
|
||||
echo "${role}:${user}" >> "$rolesFile"
|
||||
}
|
||||
|
||||
function syncElastic() {
|
||||
[[ -n $SKIP_SYNC ]] && return
|
||||
|
||||
echo "Syncing users and roles between SOC and Elastic..."
|
||||
|
||||
usersTmpFile="${elasticUsersFile}.tmp"
|
||||
createFile "${usersTmpFile}" "$esUID" "$esGID"
|
||||
rolesTmpFile="${elasticRolesFile}.tmp"
|
||||
createFile "${rolesTmpFile}" "$esUID" "$esGID"
|
||||
|
||||
authPillarJson=$(lookup_salt_value "auth" "elasticsearch" "pillar" "json")
|
||||
|
||||
syncElasticSystemUser "$authPillarJson" "so_elastic_user" "$usersTmpFile"
|
||||
syncElasticSystemUser "$authPillarJson" "so_kibana_user" "$usersTmpFile"
|
||||
syncElasticSystemUser "$authPillarJson" "so_logstash_user" "$usersTmpFile"
|
||||
syncElasticSystemUser "$authPillarJson" "so_beats_user" "$usersTmpFile"
|
||||
syncElasticSystemUser "$authPillarJson" "so_monitor_user" "$usersTmpFile"
|
||||
|
||||
syncElasticSystemRole "$authPillarJson" "so_elastic_user" "superuser" "$rolesTmpFile"
|
||||
syncElasticSystemRole "$authPillarJson" "so_kibana_user" "kibana_system" "$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
|
||||
userData=$(echo "select '{\"user\":\"' || ici.identifier || '\", \"data\":' || ic.config || '}'" \
|
||||
"from identity_credential_identifiers ici, identity_credentials ic, identities i, identity_credential_types ict " \
|
||||
"where " \
|
||||
" ici.identity_credential_id=ic.id " \
|
||||
" and ic.identity_id=i.id " \
|
||||
" and ict.id=ic.identity_credential_type_id " \
|
||||
" and ict.name='password' " \
|
||||
" and instr(ic.config, 'hashed_password') " \
|
||||
" and i.state == 'active' " \
|
||||
"order by ici.identifier;" | \
|
||||
sqlite3 -cmd ".timeout ${databaseTimeout}" "$databasePath")
|
||||
[[ $? != 0 ]] && fail "Unable to read credential hashes from database"
|
||||
echo "${userData}" | \
|
||||
jq -r '.user + ":" + .data.hashed_password' \
|
||||
>> "$usersTmpFile"
|
||||
|
||||
# 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, identity_credential_types ict " \
|
||||
"where ici.identity_credential_id=ic.id " \
|
||||
" and ict.id=ic.identity_credential_type_id " \
|
||||
" and ict.name='password' " \
|
||||
" and ic.identity_id = '$userId';" | \
|
||||
sqlite3 -cmd ".timeout ${databaseTimeout}" "$databasePath" >> "$rolesTmpFile"
|
||||
[[ $? != 0 ]] && fail "Unable to read role identities from database"
|
||||
done < "$socRolesFile"
|
||||
|
||||
else
|
||||
echo "Database file or soc roles file does not exist yet, skipping users export"
|
||||
fi
|
||||
|
||||
if [[ -s "${usersTmpFile}" ]]; then
|
||||
mv "${usersTmpFile}" "${elasticUsersFile}"
|
||||
mv "${rolesTmpFile}" "${elasticRolesFile}"
|
||||
|
||||
if [[ -z "$SKIP_STATE_APPLY" ]]; then
|
||||
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 --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."
|
||||
fi
|
||||
}
|
||||
|
||||
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
|
||||
usersFileAgeSecs=$(echo $(($(date +%s) - $(date +%s -r "$elasticUsersFile"))))
|
||||
staleCount=$(echo "select count(*) from identity_credentials where updated_at >= Datetime('now', '-${usersFileAgeSecs} seconds');" \
|
||||
| sqlite3 -cmd ".timeout ${databaseTimeout}" "$databasePath")
|
||||
[[ $? != 0 ]] && fail "Unable to read user count from database"
|
||||
if [[ "$staleCount" == "0" && "$elasticRolesFile" -nt "$socRolesFile" ]]; then
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
syncElastic
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
function listUsers() {
|
||||
response=$(curl -Ss -L ${kratosUrl}/identities)
|
||||
[[ $? != 0 ]] && fail "Unable to communicate with Kratos"
|
||||
|
||||
users=$(echo "${response}" | jq -r ".[] | .verifiable_addresses[0].value" | sort)
|
||||
for user in $users; do
|
||||
roles=$(grep ":$user\$" "$elasticRolesFile" | cut -d: -f1 | tr '\n' ' ')
|
||||
echo "$user: $roles"
|
||||
done
|
||||
}
|
||||
|
||||
function addUserRole() {
|
||||
email=$1
|
||||
role=$2
|
||||
|
||||
adjustUserRole "$email" "$role" "add"
|
||||
}
|
||||
|
||||
function deleteUserRole() {
|
||||
email=$1
|
||||
role=$2
|
||||
|
||||
adjustUserRole "$email" "$role" "del"
|
||||
}
|
||||
|
||||
function adjustUserRole() {
|
||||
email=$1
|
||||
role=$2
|
||||
op=$3
|
||||
|
||||
identityId=$(findIdByEmail "$email")
|
||||
[[ ${identityId} == "" ]] && fail "User not found"
|
||||
|
||||
ensureRoleFileExists
|
||||
|
||||
filename="$socRolesFile"
|
||||
hasRole=0
|
||||
grep "^$role:" "$socRolesFile" | grep -q "$identityId" && hasRole=1
|
||||
if [[ "$op" == "add" ]]; then
|
||||
if [[ "$hasRole" == "1" ]]; then
|
||||
echo "User '$email' already has the role: $role"
|
||||
return 1
|
||||
else
|
||||
echo "$role:$identityId" >> "$filename"
|
||||
fi
|
||||
elif [[ "$op" == "del" ]]; then
|
||||
if [[ "$hasRole" -ne 1 ]]; then
|
||||
fail "User '$email' does not have the role: $role"
|
||||
else
|
||||
sed "/^$role:$identityId\$/d" "$filename" > "$filename.tmp"
|
||||
cat "$filename".tmp > "$filename"
|
||||
rm -f "$filename".tmp
|
||||
fi
|
||||
else
|
||||
fail "Unsupported role adjustment operation: $op"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
function createUser() {
|
||||
email=$1
|
||||
role=$2
|
||||
firstName=$3
|
||||
lastName=$4
|
||||
note=$5
|
||||
|
||||
now=$(date -u +%FT%TZ)
|
||||
addUserJson=$(cat <<EOF
|
||||
{
|
||||
"traits": {
|
||||
"email": "${email}",
|
||||
"firstName": "${firstName}",
|
||||
"lastName": "${lastName}",
|
||||
"note": "${note}"
|
||||
},
|
||||
"schema_id": "default"
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
response=$(curl -Ss -L ${kratosUrl}/identities -d "$addUserJson")
|
||||
[[ $? != 0 ]] && fail "Unable to communicate with Kratos"
|
||||
|
||||
identityId=$(echo "${response}" | jq -r ".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}"
|
||||
else
|
||||
updatePassword "$identityId"
|
||||
addUserRole "$email" "$role"
|
||||
fi
|
||||
}
|
||||
|
||||
function updateStatus() {
|
||||
email=$1
|
||||
status=$2
|
||||
|
||||
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)
|
||||
|
||||
# Capture traits and remove obsolete 'status' trait if exists
|
||||
traitBlock=$(echo "$response" | jq -c .traits | sed -re 's/,?"status":".*?"//')
|
||||
|
||||
state="active"
|
||||
if [[ "$status" == "locked" ]]; then
|
||||
state="inactive"
|
||||
fi
|
||||
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 updateUserPassword() {
|
||||
email=$1
|
||||
|
||||
identityId=$(findIdByEmail "$email")
|
||||
[[ ${identityId} == "" ]] && fail "User not found"
|
||||
|
||||
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
|
||||
|
||||
identityId=$(findIdByEmail "$email")
|
||||
[[ ${identityId} == "" ]] && fail "User not found"
|
||||
|
||||
response=$(curl -Ss -XDELETE -L "${kratosUrl}/identities/$identityId")
|
||||
[[ $? != 0 ]] && fail "Unable to communicate with Kratos"
|
||||
|
||||
rolesTmpFile="${socRolesFile}.tmp"
|
||||
createFile "$rolesTmpFile" "$soUID" "$soGID"
|
||||
grep -v "$identityId" "$socRolesFile" > "$rolesTmpFile"
|
||||
cat "$rolesTmpFile" > "$socRolesFile"
|
||||
}
|
||||
|
||||
case "${operation}" in
|
||||
"add")
|
||||
verifyEnvironment
|
||||
[[ "$email" == "" ]] && fail "Email address must be provided"
|
||||
|
||||
lock
|
||||
validateEmail "$email"
|
||||
updatePassword
|
||||
createUser "$email" "${role:-$DEFAULT_ROLE}" "${firstName}" "${lastName}" "${note}"
|
||||
syncAll
|
||||
echo "Successfully added new user to SOC"
|
||||
echo "$password" | so-influxdb-manage useradd "$email"
|
||||
if [[ "$role" == "superuser" ]]; then
|
||||
echo "$password" | so-influxdb-manage userpromote "$email"
|
||||
fi
|
||||
;;
|
||||
|
||||
"list")
|
||||
verifyEnvironment
|
||||
listUsers
|
||||
;;
|
||||
|
||||
"addrole")
|
||||
verifyEnvironment
|
||||
[[ "$email" == "" ]] && fail "Email address must be provided"
|
||||
[[ "$role" == "" ]] && fail "Role must be provided"
|
||||
|
||||
lock
|
||||
validateEmail "$email"
|
||||
if addUserRole "$email" "$role"; then
|
||||
syncElastic
|
||||
echo "Successfully added role to user"
|
||||
if [[ "$role" == "superuser" ]]; then
|
||||
echo "$password" | so-influxdb-manage userpromote "$email"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
|
||||
"delrole")
|
||||
verifyEnvironment
|
||||
[[ "$email" == "" ]] && fail "Email address must be provided"
|
||||
[[ "$role" == "" ]] && fail "Role must be provided"
|
||||
|
||||
lock
|
||||
validateEmail "$email"
|
||||
deleteUserRole "$email" "$role"
|
||||
syncElastic
|
||||
echo "Successfully removed role from user"
|
||||
if [[ "$role" == "superuser" ]]; then
|
||||
echo "$password" | so-influxdb-manage userdemote "$email"
|
||||
fi
|
||||
;;
|
||||
|
||||
"password")
|
||||
verifyEnvironment
|
||||
[[ "$email" == "" ]] && fail "Email address must be provided"
|
||||
|
||||
lock
|
||||
updateUserPassword "$email"
|
||||
syncAll
|
||||
echo "Successfully updated user password"
|
||||
echo "$password" | so-influxdb-manage userpass "$email"
|
||||
;;
|
||||
|
||||
"profile")
|
||||
verifyEnvironment
|
||||
[[ "$email" == "" ]] && fail "Email address must be provided"
|
||||
|
||||
lock
|
||||
updateUserProfile "$email"
|
||||
echo "Successfully updated user profile"
|
||||
;;
|
||||
|
||||
"enable")
|
||||
verifyEnvironment
|
||||
[[ "$email" == "" ]] && fail "Email address must be provided"
|
||||
|
||||
lock
|
||||
updateStatus "$email" 'active'
|
||||
syncAll
|
||||
echo "Successfully enabled user"
|
||||
so-influxdb-manage userenable "$email"
|
||||
;;
|
||||
|
||||
"disable")
|
||||
verifyEnvironment
|
||||
[[ "$email" == "" ]] && fail "Email address must be provided"
|
||||
|
||||
lock
|
||||
updateStatus "$email" 'locked'
|
||||
syncAll
|
||||
echo "Successfully disabled user"
|
||||
so-influxdb-manage userdisable "$email"
|
||||
;;
|
||||
|
||||
"delete")
|
||||
verifyEnvironment
|
||||
[[ "$email" == "" ]] && fail "Email address must be provided"
|
||||
|
||||
lock
|
||||
deleteUser "$email"
|
||||
syncAll
|
||||
echo "Successfully deleted user"
|
||||
so-influxdb-manage userdel "$email"
|
||||
;;
|
||||
|
||||
"sync")
|
||||
lock
|
||||
syncAll
|
||||
;;
|
||||
|
||||
"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"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
2
salt/manager/tools/sbin/so-user-add
Executable file
2
salt/manager/tools/sbin/so-user-add
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
so-user add --email $1
|
||||
2
salt/manager/tools/sbin/so-user-disable
Executable file
2
salt/manager/tools/sbin/so-user-disable
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
so-user disable --email $1
|
||||
2
salt/manager/tools/sbin/so-user-enable
Executable file
2
salt/manager/tools/sbin/so-user-enable
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
so-user enable --email $1
|
||||
2
salt/manager/tools/sbin/so-user-list
Executable file
2
salt/manager/tools/sbin/so-user-list
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
so-user list
|
||||
1478
salt/manager/tools/sbin/soup
Executable file
1478
salt/manager/tools/sbin/soup
Executable file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user