From 6f391dbe50922c343c0c4b31900a2737ae0beb28 Mon Sep 17 00:00:00 2001 From: Josh Brower Date: Wed, 17 Nov 2021 13:13:25 -0500 Subject: [PATCH] Migrate FleetDM user mgt to fleetctl --- salt/common/tools/sbin/so-fleet-setup | 7 +++ salt/common/tools/sbin/so-fleet-user-add | 29 ++++++----- salt/common/tools/sbin/so-fleet-user-delete | 56 ++++++++++++++++++++ salt/common/tools/sbin/so-fleet-user-enable | 58 --------------------- salt/common/tools/sbin/so-user | 6 +-- salt/common/tools/sbin/soup | 36 +++++++++++++ salt/fleet/files/packs/osquery-config.conf | 57 ++++++++++---------- setup/so-functions | 3 ++ 8 files changed, 150 insertions(+), 102 deletions(-) create mode 100644 salt/common/tools/sbin/so-fleet-user-delete delete mode 100755 salt/common/tools/sbin/so-fleet-user-enable diff --git a/salt/common/tools/sbin/so-fleet-setup b/salt/common/tools/sbin/so-fleet-setup index 6570862c7..18c97b2f0 100755 --- a/salt/common/tools/sbin/so-fleet-setup +++ b/salt/common/tools/sbin/so-fleet-setup @@ -2,6 +2,8 @@ #so-fleet-setup $FleetEmail $FleetPassword +. /usr/sbin/so-common + if [[ $# -ne 2 ]] ; then echo "Username or Password was not set - exiting now." exit 1 @@ -19,6 +21,11 @@ docker exec so-fleet fleetctl config set --address https://127.0.0.1:8080 --tls- docker exec so-fleet bash -c 'while [[ "$(curl -s -o /dev/null --insecure -w ''%{http_code}'' https://127.0.0.1:8080/fleet)" != "301" ]]; do sleep 5; done' docker exec so-fleet fleetctl setup --email $1 --password $2 --name admin --org-name SO +# Create Security Onion Fleet Service Account +FLEET_SA_EMAIL=$(lookup_pillar_secret fleet_sa_email) +FLEET_SA_PW=$(lookup_pillar_secret fleet_sa_password) +docker exec so-fleet fleetctl user create --email $FLEET_SA_EMAIL --name SO_ServiceAccount --password $FLEET_SA_PW --global-role admin + docker exec so-fleet fleetctl apply -f /packs/palantir/Fleet/Endpoints/MacOS/osquery.yaml docker exec so-fleet fleetctl apply -f /packs/palantir/Fleet/Endpoints/Windows/osquery.yaml docker exec so-fleet fleetctl apply -f /packs/so/so-default.yml diff --git a/salt/common/tools/sbin/so-fleet-user-add b/salt/common/tools/sbin/so-fleet-user-add index 0fc028b3d..12fb6d87d 100755 --- a/salt/common/tools/sbin/so-fleet-user-add +++ b/salt/common/tools/sbin/so-fleet-user-add @@ -18,7 +18,7 @@ . /usr/sbin/so-common usage() { - echo "Usage: $0 " + echo "Usage: $0 " echo "" echo "Adds a new user to Fleet. The new password will be read from STDIN." exit 1 @@ -28,34 +28,37 @@ if [ $# -ne 1 ]; then usage fi -USER=$1 -MYSQL_PASS=$(lookup_pillar_secret mysql) -FLEET_IP=$(lookup_pillar fleet_ip) -FLEET_USER=$USER +USER_EMAIL=$1 +FLEET_SA_EMAIL=$(lookup_pillar_secret fleet_sa_email) +FLEET_SA_PW=$(lookup_pillar_secret fleet_sa_password) # Read password for new user from stdin test -t 0 if [[ $? == 0 ]]; then echo "Enter new password:" fi -read -rs FLEET_PASS +read -rs USER_PASS -check_password_and_exit "$FLEET_PASS" +check_password_and_exit "$USER_PASS" + +# Config fleetctl & login with the SO Service Account +CONFIG_OUTPUT=$(docker exec so-fleet fleetctl config set --address https://127.0.0.1:8080 --tls-skip-verify --url-prefix /fleet 2>&1 ) +SALOGIN_OUTPUT=$(docker exec so-fleet fleetctl login --email $FLEET_SA_EMAIL --password $FLEET_SA_PW 2>&1) -FLEET_HASH=$(docker exec so-soctopus python -c "import bcrypt; print(bcrypt.hashpw('$FLEET_PASS'.encode('utf-8'), bcrypt.gensalt()).decode('utf-8'));" 2>&1) if [[ $? -ne 0 ]]; then - echo "Failed to generate Fleet password hash" - exit 2 + echo "Unable to add user to Fleet; Fleet Service account login failed" + echo "$SALOGIN_OUTPUT" + exit 2 fi -MYSQL_OUTPUT=$(docker exec so-mysql mysql -u root --password=$MYSQL_PASS fleet -e \ - "INSERT INTO users (password,salt,email,admin,enabled) VALUES ('$FLEET_HASH','','$FLEET_USER',1,1)" 2>&1) +# Create New User +CREATE_OUTPUT=$(docker exec so-fleet fleetctl user create --email $USER_EMAIL --name $USER_EMAIL --password $USER_PASS --global-role admin 2>&1) if [[ $? -eq 0 ]]; then echo "Successfully added user to Fleet" else echo "Unable to add user to Fleet; user might already exist" - echo "$MYSQL_OUTPUT" + echo "$CREATE_OUTPUT" exit 2 fi \ No newline at end of file diff --git a/salt/common/tools/sbin/so-fleet-user-delete b/salt/common/tools/sbin/so-fleet-user-delete new file mode 100644 index 000000000..3d07e2aa5 --- /dev/null +++ b/salt/common/tools/sbin/so-fleet-user-delete @@ -0,0 +1,56 @@ +#!/bin/bash +# +# Copyright 2014,2015,2016,2017,2018,2019,2020,2021 Security Onion Solutions, LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# 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. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +. /usr/sbin/so-common + +usage() { + echo "Usage: $0 " + echo "" + echo "Deletes a user in Fleet" + exit 1 +} + +if [ $# -ne 1 ]; then + usage +fi + +USER_EMAIL=$1 +FLEET_SA_EMAIL=$(lookup_pillar_secret fleet_sa_email) +FLEET_SA_PW=$(lookup_pillar_secret fleet_sa_password) + +# Config fleetctl & login with the SO Service Account +CONFIG_OUTPUT=$(docker exec so-fleet fleetctl config set --address https://127.0.0.1:8080 --tls-skip-verify --url-prefix /fleet 2>&1 ) +SALOGIN_OUTPUT=$(docker exec so-fleet fleetctl login --email $FLEET_SA_EMAIL --password $FLEET_SA_PW 2>&1) + +if [[ $? -ne 0 ]]; then + echo "Unable to delete user from Fleet; Fleet Service account login failed" + echo "$SALOGIN_OUTPUT" + exit 2 +fi + +# Delete User +DELETE_OUTPUT=$(docker exec so-fleet fleetctl user delete --email $USER_EMAIL 2>&1) + +if [[ $? -eq 0 ]]; then + echo "Successfully deleted user from Fleet" +else + echo "Unable to delete user from Fleet" + echo "$DELETE_OUTPUT" + exit 2 +fi + + diff --git a/salt/common/tools/sbin/so-fleet-user-enable b/salt/common/tools/sbin/so-fleet-user-enable deleted file mode 100755 index 342620a54..000000000 --- a/salt/common/tools/sbin/so-fleet-user-enable +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash -# -# Copyright 2014,2015,2016,2017,2018,2019,2020,2021 Security Onion Solutions, LLC -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# 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. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -. /usr/sbin/so-common - -usage() { - echo "Usage: $0 " - echo "" - echo "Enables or disables a user in Fleet" - exit 1 -} - -if [ $# -ne 2 ]; then - usage -fi - -USER=$1 - -MYSQL_PASS=$(lookup_pillar_secret mysql) -FLEET_IP=$(lookup_pillar fleet_ip) -FLEET_USER=$USER - -case "${2^^}" in - FALSE | NO | 0) - FLEET_STATUS=0 - ;; - TRUE | YES | 1) - FLEET_STATUS=1 - ;; - *) - usage - ;; -esac - -MYSQL_OUTPUT=$(docker exec so-mysql mysql -u root --password=$MYSQL_PASS fleet -e \ - "UPDATE users SET enabled=$FLEET_STATUS WHERE email='$FLEET_USER'" 2>&1) - -if [[ $? -eq 0 ]]; then - echo "Successfully updated user in Fleet" -else - echo "Failed to update user in Fleet" - echo $resp - exit 2 -fi \ No newline at end of file diff --git a/salt/common/tools/sbin/so-user b/salt/common/tools/sbin/so-user index 9bf36cf99..cf9fc91c0 100755 --- a/salt/common/tools/sbin/so-user +++ b/salt/common/tools/sbin/so-user @@ -517,7 +517,7 @@ case "${operation}" in syncAll echo "Successfully enabled user" check_container thehive && so-thehive-user-enable "$email" true - check_container fleet && so-fleet-user-enable "$email" true + echo "Fleet user will need to be recreated manually with so-fleet-user-add" ;; "disable") @@ -529,7 +529,7 @@ case "${operation}" in syncAll echo "Successfully disabled user" check_container thehive && so-thehive-user-enable "$email" false - check_container fleet && so-fleet-user-enable "$email" false + check_container fleet && so-fleet-user-delete "$email" ;; "delete") @@ -541,7 +541,7 @@ case "${operation}" in syncAll echo "Successfully deleted user" check_container thehive && so-thehive-user-enable "$email" false - check_container fleet && so-fleet-user-enable "$email" false + check_container fleet && so-fleet-user-delete "$email" ;; "sync") diff --git a/salt/common/tools/sbin/soup b/salt/common/tools/sbin/soup index 087b7c295..a27ed88d9 100755 --- a/salt/common/tools/sbin/soup +++ b/salt/common/tools/sbin/soup @@ -439,6 +439,29 @@ post_to_2.3.90() { # Do Kibana dashboard things salt-call state.apply kibana.so_savedobjects_defaults queue=True + # Create FleetDM service account + FLEET_SA_EMAIL=$(lookup_pillar_secret fleet_sa_email) + FLEET_SA_PW=$(lookup_pillar_secret fleet_sa_password) + MYSQL_PW=$(lookup_pillar_secret mysql) + + FLEET_HASH=$(docker exec so-soctopus python -c "import bcrypt; print(bcrypt.hashpw('$FLEET_SA_PW'.encode('utf-8'), bcrypt.gensalt()).decode('utf-8'));" 2>&1) + + if [[ $? -ne 0 ]]; then + echo "Failed to generate Fleet password hash" + exit 2 + fi + + MYSQL_OUTPUT=$(docker exec so-mysql mysql -u root --password=$MYSQL_PW fleet -e \ + "INSERT INTO users (password,salt,email,name,global_role) VALUES ('$FLEET_HASH','','$FLEET_USER','$FLEET_USER','admin')" 2>&1) + + if [[ $? -eq 0 ]]; then + echo "Successfully added service account to Fleet" + else + echo "Unable to add service account to Fleet" + echo "$MYSQL_OUTPUT" + exit 2 + fi + POSTVERSION=2.3.90 } @@ -600,6 +623,19 @@ up_to_2.3.90() { sed -i -e '$a{{'{% endraw %}'}}\n' /opt/so/saltstack/local/salt/elasticsearch/files/ingest-dynamic/common fi + # Generate FleetDM Service Account creds if they do not exist + if grep -q "fleet_sa_email" /opt/so/saltstack/local/pillar/secrets.sls; then + echo "FleetDM Service Account credentials already created..." + else + echo "Generating FleetDM Service Account credentials..." + FLEETSAPASS=$(get_random_value) + printf '%s\n'\ + " fleet_sa_email: service.account@securityonion.invalid"\ + " fleet_sa_password: $FLEETSAPASS"\ + >> /opt/so/saltstack/local/pillar/secrets.sls + + fi + INSTALLEDVERSION=2.3.90 } diff --git a/salt/fleet/files/packs/osquery-config.conf b/salt/fleet/files/packs/osquery-config.conf index 99cbe2197..79d691b4e 100644 --- a/salt/fleet/files/packs/osquery-config.conf +++ b/salt/fleet/files/packs/osquery-config.conf @@ -4,31 +4,32 @@ kind: config spec: server_settings: enable_analytics: false -config: - decorators: - always: - - SELECT codename FROM os_version; - - SELECT uuid AS live_query FROM system_info; - - SELECT address AS endpoint_ip1 FROM interface_addresses where address not - like '%:%' and address not like '127%' and address not like '169%' order by - interface desc limit 1; - - SELECT address AS endpoint_ip2 FROM interface_addresses where address not - like '%:%' and address not like '127%' and address not like '169%' order by - interface asc limit 1; - - SELECT hardware_serial FROM system_info; - - SELECT hostname AS hostname FROM system_info; - options: - decorations_top_level: true - disable_distributed: false - distributed_interval: 10 - distributed_plugin: tls - distributed_tls_max_attempts: 3 - distributed_tls_read_endpoint: /api/v1/osquery/distributed/read - distributed_tls_write_endpoint: /api/v1/osquery/distributed/write - enable_windows_events_publisher: true - enable_windows_events_subscriber: true - logger_plugin: tls - logger_tls_endpoint: /api/v1/osquery/log - logger_tls_period: 10 - pack_delimiter: _ -overrides: {} +spec: + agent_options: + config: + decorators: + always: + - SELECT codename FROM os_version; + - SELECT uuid AS live_query FROM system_info; + - SELECT address AS endpoint_ip1 FROM interface_addresses where address not + like '%:%' and address not like '127%' and address not like '169%' order by + interface desc limit 1; + - SELECT address AS endpoint_ip2 FROM interface_addresses where address not + like '%:%' and address not like '127%' and address not like '169%' order by + interface asc limit 1; + - SELECT hardware_serial FROM system_info; + - SELECT hostname AS hostname FROM system_info; + options: + decorations_top_level: true + disable_distributed: false + distributed_interval: 10 + distributed_plugin: tls + distributed_tls_max_attempts: 3 + distributed_tls_read_endpoint: /api/v1/osquery/distributed/read + distributed_tls_write_endpoint: /api/v1/osquery/distributed/write + enable_windows_events_publisher: true + enable_windows_events_subscriber: true + logger_plugin: tls + logger_tls_endpoint: /api/v1/osquery/log + logger_tls_period: 10 + pack_delimiter: _ \ No newline at end of file diff --git a/setup/so-functions b/setup/so-functions index 627ec934f..95d48194f 100755 --- a/setup/so-functions +++ b/setup/so-functions @@ -1440,6 +1440,7 @@ generate_passwords(){ PLAYBOOKADMINPASS=$(get_random_value) PLAYBOOKAUTOMATIONPASS=$(get_random_value) FLEETPASS=$(get_random_value) + FLEETSAPASS=$(get_random_value) FLEETJWT=$(get_random_value) GRAFANAPASS=$(get_random_value) if [[ "$THEHIVE" == "1" ]]; then @@ -2369,6 +2370,8 @@ secrets_pillar(){ " playbook_automation: $PLAYBOOKAUTOMATIONPASS"\ " grafana_admin: $GRAFANAPASS"\ " fleet: $FLEETPASS"\ + " fleet_sa_email: service.account@securityonion.invalid"\ + " fleet_sa_password: $FLEETSAPASS"\ " fleet_jwt: $FLEETJWT"\ " fleet_enroll-secret: False" > $local_salt_dir/pillar/secrets.sls fi