crowdsec/wizard.sh
Laurence Jones ecd82ecfbd
feat: File notification plugin (#2932)
* wip: basic impl of file notification no log rotate but might now do it 🤷

* wip: ticker to 2 seconds and lower some log levels

* wip: remove redundant logrus formatter

* wip: the plugin should not handle it own data queue since the plugin process may timeout, so instead have a function that uses said context and loop whilst locking the filewriter this may not be the best way 🤷, however, I dont want multiple notifications to attempt to reopen the file if it has been rotated outside of the plugin context

* wip: impl log rotation which checks on check append, however, this may cause some issues in slow systems as the mutex lock doesnt give up until the file is rotated, however, the plugin looks for context and will give up if the plugin broker decides its timeout and will retry once the plugin has pushed again

* wip: update yaml dep

* wip: me no english great

* wip: even if the file has been rotated outside our control we should still compute the file size

* wip: improve context handling with creating a custom io writer struct which checks the context before attempting to write

* wip: used return byte count instead of calling a conversion again

* wip: actually check the enabled flag on log rotate

* wip: changed my mind, we check when we check file size

* wip: use io copy instead for memory alloc

* fix: add notification file to deb/rpm build
2024-05-15 09:21:45 +01:00

837 lines
28 KiB
Bash
Executable file

#!/usr/bin/env bash
set -o pipefail
#set -x
skip_tmp_acquis() {
[[ "${TMP_ACQUIS_FILE_SKIP}" == "skip" ]]
}
RED='\033[0;31m'
BLUE='\033[0;34m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
ORANGE='\033[0;33m'
NC='\033[0m'
SILENT="false"
DOCKER_MODE="false"
CROWDSEC_RUN_DIR="/var/run"
CROWDSEC_LIB_DIR="/var/lib/crowdsec"
CROWDSEC_USR_DIR="/usr/local/lib/crowdsec"
CROWDSEC_DATA_DIR="${CROWDSEC_LIB_DIR}/data"
CROWDSEC_DB_PATH="${CROWDSEC_DATA_DIR}/crowdsec.db"
CROWDSEC_PATH="/etc/crowdsec"
CROWDSEC_CONFIG_PATH="${CROWDSEC_PATH}"
CROWDSEC_LOG_FILE="/var/log/crowdsec.log"
LAPI_LOG_FILE="/var/log/crowdsec_api.log"
CROWDSEC_PLUGIN_DIR="${CROWDSEC_USR_DIR}/plugins"
CROWDSEC_CONSOLE_DIR="${CROWDSEC_PATH}/console"
CROWDSEC_BIN="./cmd/crowdsec/crowdsec"
CSCLI_BIN="./cmd/crowdsec-cli/cscli"
CLIENT_SECRETS="local_api_credentials.yaml"
LAPI_SECRETS="online_api_credentials.yaml"
CONSOLE_FILE="console.yaml"
BIN_INSTALL_PATH="/usr/local/bin"
CROWDSEC_BIN_INSTALLED="${BIN_INSTALL_PATH}/crowdsec"
if [[ -f "/usr/bin/cscli" ]] ; then
CSCLI_BIN_INSTALLED="/usr/bin/cscli"
else
CSCLI_BIN_INSTALLED="${BIN_INSTALL_PATH}/cscli"
fi
ACQUIS_PATH="${CROWDSEC_CONFIG_PATH}"
ACQUIS_TARGET="${ACQUIS_PATH}/acquis.yaml"
SYSTEMD_PATH_FILE="/etc/systemd/system/crowdsec.service"
PATTERNS_FOLDER="config/patterns"
PATTERNS_PATH="${CROWDSEC_CONFIG_PATH}/patterns/"
ACTION=""
DEBUG_MODE="false"
FORCE_MODE="false"
# the ssh service has different names on deb vs rpm-based distros
if [[ -f "/etc/debian_version" ]]; then
SSH_NAME="ssh"
else
SSH_NAME="sshd"
fi
SUPPORTED_SERVICES="apache2
httpd
nginx
$SSH_NAME
mysql
telnet
smb
"
HTTP_PLUGIN_BINARY="./cmd/notification-http/notification-http"
SLACK_PLUGIN_BINARY="./cmd/notification-slack/notification-slack"
SPLUNK_PLUGIN_BINARY="./cmd/notification-splunk/notification-splunk"
EMAIL_PLUGIN_BINARY="./cmd/notification-email/notification-email"
SENTINEL_PLUGIN_BINARY="./cmd/notification-sentinel/notification-sentinel"
FILE_PLUGIN_BINARY="./cmd/notification-file/notification-file"
HTTP_PLUGIN_CONFIG="./cmd/notification-http/http.yaml"
SLACK_PLUGIN_CONFIG="./cmd/notification-slack/slack.yaml"
SPLUNK_PLUGIN_CONFIG="./cmd/notification-splunk/splunk.yaml"
EMAIL_PLUGIN_CONFIG="./cmd/notification-email/email.yaml"
SENTINEL_PLUGIN_CONFIG="./cmd/notification-sentinel/sentinel.yaml"
FILE_PLUGIN_CONFIG="./cmd/notification-file/file.yaml"
BACKUP_DIR=$(mktemp -d)
rm -rf -- "$BACKUP_DIR"
log_info() {
msg=$1
date=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "${BLUE}INFO${NC}[${date}] crowdsec_wizard: ${msg}"
}
log_fatal() {
msg=$1
date=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "${RED}FATA${NC}[${date}] crowdsec_wizard: ${msg}" 1>&2
exit 1
}
log_warn() {
msg=$1
date=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "${ORANGE}WARN${NC}[${date}] crowdsec_wizard: ${msg}"
}
log_err() {
msg=$1
date=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "${RED}ERR${NC}[${date}] crowdsec_wizard: ${msg}" 1>&2
}
log_dbg() {
if [[ ${DEBUG_MODE} == "true" ]]; then
msg=$1
date=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "[${date}][${YELLOW}DBG${NC}] crowdsec_wizard: ${msg}" 1>&2
fi
}
detect_services () {
DETECTED_SERVICES=()
HMENU=()
# list systemd services
SYSTEMD_SERVICES=$(systemctl --state=enabled list-unit-files '*.service' | cut -d ' ' -f1)
# raw ps
PSAX=$(ps ax -o comm=)
for SVC in ${SUPPORTED_SERVICES} ; do
log_dbg "Checking if service '${SVC}' is running (ps+systemd)"
for SRC in "${SYSTEMD_SERVICES}" "${PSAX}" ; do
echo ${SRC} | grep ${SVC} >/dev/null
if [ $? -eq 0 ]; then
# on centos, apache2 is named httpd
if [[ ${SVC} == "httpd" ]] ; then
SVC="apache2";
fi
DETECTED_SERVICES+=(${SVC})
HMENU+=(${SVC} "on")
log_dbg "Found '${SVC}' running"
break;
fi;
done;
done;
if [[ ${OSTYPE} == "linux-gnu" ]] || [[ ${OSTYPE} == "linux-gnueabihf" ]]; then
DETECTED_SERVICES+=("linux")
HMENU+=("linux" "on")
else
log_info "NOT A LINUX"
fi;
if [[ ${SILENT} == "false" ]]; then
# we put whiptail results in an array, notice the dark magic fd redirection
DETECTED_SERVICES=($(whiptail --separate-output --noitem --ok-button Continue --title "Services to monitor" --checklist "Detected services, uncheck to ignore. Ignored services won't be monitored." 18 70 10 ${HMENU[@]} 3>&1 1>&2 2>&3))
if [ $? -eq 1 ]; then
log_err "user bailed out at services selection"
exit 1;
fi;
log_dbg "Detected services (interactive) : ${DETECTED_SERVICES[@]}"
else
log_dbg "Detected services (unattended) : ${DETECTED_SERVICES[@]}"
fi;
}
declare -A log_input_tags
log_input_tags[apache2]='type: apache2'
log_input_tags[nginx]='type: nginx'
log_input_tags[$SSH_NAME]='type: syslog'
log_input_tags[rsyslog]='type: syslog'
log_input_tags[telnet]='type: telnet'
log_input_tags[mysql]='type: mysql'
log_input_tags[smb]='type: smb'
log_input_tags[linux]="type: syslog"
declare -A log_locations
log_locations[apache2]='/var/log/apache2/*.log,/var/log/*httpd*.log,/var/log/httpd/*log'
log_locations[nginx]='/var/log/nginx/*.log,/usr/local/openresty/nginx/logs/*.log'
log_locations[$SSH_NAME]='/var/log/auth.log,/var/log/sshd.log,/var/log/secure'
log_locations[rsyslog]='/var/log/syslog'
log_locations[telnet]='/var/log/telnetd*.log'
log_locations[mysql]='/var/log/mysql/error.log'
log_locations[smb]='/var/log/samba*.log'
log_locations[linux]='/var/log/syslog,/var/log/kern.log,/var/log/messages'
# $1 is service name, such those in SUPPORTED_SERVICES
find_logs_for() {
x=${1}
# we have trailing and starting quotes because of whiptail
SVC="${x%\"}"
SVC="${SVC#\"}"
DETECTED_LOGFILES=()
HMENU=()
# log_info "Searching logs for ${SVC} : ${log_locations[${SVC}]}"
# split the line into an array with ',' separator
OIFS=${IFS}
IFS=',' read -r -a a <<< "${log_locations[${SVC}]},"
IFS=${OIFS}
# readarray -td, a <<<"${log_locations[${SVC}]},"; unset 'a[-1]';
for poss_path in "${a[@]}"; do
# Split /var/log/nginx/*.log into '/var/log/nginx' and '*.log' so we can use find
path=${poss_path%/*}
fname=${poss_path##*/}
candidates=$(find "${path}" -type f -mtime -5 -ctime -5 -name "$fname" 2>/dev/null)
# We have some candidates, add them
for final_file in ${candidates} ; do
log_dbg "Found logs file for '${SVC}': ${final_file}"
DETECTED_LOGFILES+=(${final_file})
HMENU+=(${final_file} "on")
done;
done;
if [[ ${SILENT} == "false" ]]; then
DETECTED_LOGFILES=($(whiptail --separate-output --noitem --ok-button Continue --title "Log files to process for ${SVC}" --checklist "Detected logfiles for ${SVC}, uncheck to ignore" 18 70 10 ${HMENU[@]} 3>&1 1>&2 2>&3))
if [ $? -eq 1 ]; then
log_err "user bailed out at log file selection"
exit 1;
fi;
fi
}
in_array() {
str=$1
shift
array=("$@")
for element in "${array[@]}"; do
if [[ ${str} == crowdsecurity/${element} ]]; then
return 0
fi
done
return 1
}
install_collection() {
HMENU=()
readarray -t AVAILABLE_COLLECTION < <(${CSCLI_BIN_INSTALLED} collections list -o raw -a)
COLLECTION_TO_INSTALL=()
for collect_info in "${AVAILABLE_COLLECTION[@]:1}"; do
collection="$(echo ${collect_info} | cut -d "," -f1)"
description="$(echo ${collect_info} | cut -d "," -f4)"
in_array $collection "${DETECTED_SERVICES[@]}"
if [[ $? == 0 ]]; then
HMENU+=("${collection}" "${description}" "ON")
# in case we're not in interactive mode, assume defaults
COLLECTION_TO_INSTALL+=(${collection})
else
if [[ ${collection} == "linux" ]]; then
HMENU+=("${collection}" "${description}" "ON")
# in case we're not in interactive mode, assume defaults
COLLECTION_TO_INSTALL+=(${collection})
else
HMENU+=("${collection}" "${description}" "OFF")
fi
fi
done
if [[ ${SILENT} == "false" ]]; then
COLLECTION_TO_INSTALL=($(whiptail --separate-output --ok-button Continue --title "Crowdsec collections" --checklist "Available collections in crowdsec, try to pick one that fits your profile. Collections contains parsers and scenarios to protect your system." 20 120 10 "${HMENU[@]}" 3>&1 1>&2 2>&3))
if [ $? -eq 1 ]; then
log_err "user bailed out at collection selection"
exit 1;
fi;
fi;
for collection in "${COLLECTION_TO_INSTALL[@]}"; do
log_info "Installing collection '${collection}'"
${CSCLI_BIN_INSTALLED} collections install "${collection}" --error
done
${CSCLI_BIN_INSTALLED} parsers install "crowdsecurity/whitelists" --error
if [[ ${SILENT} == "false" ]]; then
whiptail --msgbox "Out of safety, I installed a parser called 'crowdsecurity/whitelists'. This one will prevent private IP addresses from being banned, feel free to remove it any time." 20 50
fi
if [[ ${SILENT} == "false" ]]; then
whiptail --msgbox "CrowdSec alone will not block any IP address. If you want to block them, you must use a bouncer. You can find them on https://hub.crowdsec.net/browse/#bouncers" 20 50
fi
}
# $1 is the service name, $... is the list of candidate logs (from find_logs_for)
genyamllog() {
local service="${1}"
shift
local files=("${@}")
echo "#Generated acquisition file - wizard.sh (service: ${service}) / files : ${files[@]}" >> ${TMP_ACQUIS_FILE}
echo "filenames:" >> ${TMP_ACQUIS_FILE}
for fd in ${files[@]}; do
echo " - ${fd}" >> ${TMP_ACQUIS_FILE}
done
echo "labels:" >> ${TMP_ACQUIS_FILE}
echo " "${log_input_tags[${service}]} >> ${TMP_ACQUIS_FILE}
echo "---" >> ${TMP_ACQUIS_FILE}
log_dbg "${ACQUIS_FILE_MSG}"
}
genyamljournal() {
local service="${1}"
shift
echo "#Generated acquisition file - wizard.sh (service: ${service}) / files : ${files[@]}" >> ${TMP_ACQUIS_FILE}
echo "journalctl_filter:" >> ${TMP_ACQUIS_FILE}
echo " - _SYSTEMD_UNIT="${service}".service" >> ${TMP_ACQUIS_FILE}
echo "labels:" >> ${TMP_ACQUIS_FILE}
echo " "${log_input_tags[${service}]} >> ${TMP_ACQUIS_FILE}
echo "---" >> ${TMP_ACQUIS_FILE}
log_dbg "${ACQUIS_FILE_MSG}"
}
genacquisition() {
if skip_tmp_acquis; then
TMP_ACQUIS_FILE="${ACQUIS_TARGET}"
ACQUIS_FILE_MSG="acquisition file generated to: ${TMP_ACQUIS_FILE}"
else
TMP_ACQUIS_FILE="tmp-acquis.yaml"
ACQUIS_FILE_MSG="tmp acquisition file generated to: ${TMP_ACQUIS_FILE}"
fi
log_dbg "Found following services : "${DETECTED_SERVICES[@]}
for PSVG in ${DETECTED_SERVICES[@]} ; do
find_logs_for ${PSVG}
if [[ ${#DETECTED_LOGFILES[@]} -gt 0 ]] ; then
log_info "service '${PSVG}': ${DETECTED_LOGFILES[*]}"
genyamllog ${PSVG} ${DETECTED_LOGFILES[@]}
elif [[ ${PSVG} != "linux" ]] ; then
log_info "using journald for '${PSVG}'"
genyamljournal ${PSVG}
fi;
done
}
detect_cs_install () {
if [[ -f "$CROWDSEC_BIN_INSTALLED" ]]; then
log_warn "Crowdsec is already installed !"
echo ""
echo "We recommend to upgrade : sudo ./wizard.sh --upgrade "
echo "If you want to install it anyway, please use '--force'."
echo ""
echo "Run : sudo ./wizard.sh -i --force"
if [[ ${FORCE_MODE} == "false" ]]; then
exit 1
fi
fi
}
check_cs_version () {
CURRENT_CS_VERSION=$(crowdsec -version 2>&1 | grep version | grep -Eio 'v[0-9]+.[0-9]+.[0-9]+' | cut -c 2-)
NEW_CS_VERSION=$($CROWDSEC_BIN -version 2>&1 | grep version | grep -Eio 'v[0-9]+.[0-9]+.[0-9]+' | cut -c 2-)
CURRENT_MAJOR_VERSION=$(echo $CURRENT_CS_VERSION | cut -d'.' -f1)
CURRENT_MINOR_VERSION=$(echo $CURRENT_CS_VERSION | cut -d'.' -f2)
CURRENT_PATCH_VERSION=$(echo $CURRENT_CS_VERSION | cut -d'.' -f3)
NEW_MAJOR_VERSION=$(echo $NEW_CS_VERSION | cut -d'.' -f1)
NEW_MINOR_VERSION=$(echo $NEW_CS_VERSION | cut -d'.' -f2)
NEW_PATCH_VERSION=$(echo $NEW_CS_VERSION | cut -d'.' -f3)
if [[ $NEW_MAJOR_VERSION -gt $CURRENT_MAJOR_VERSION ]]; then
if [[ ${FORCE_MODE} == "false" ]]; then
log_warn "new version ($NEW_CS_VERSION) is a major, you should follow documentation to upgrade !"
echo ""
exit 1
fi
elif [[ $NEW_MINOR_VERSION -gt $CURRENT_MINOR_VERSION ]] ; then
log_warn "new version ($NEW_CS_VERSION) is a minor upgrade !"
if [[ $ACTION != "upgrade" ]] ; then
if [[ ${FORCE_MODE} == "false" ]]; then
echo ""
echo "We recommend to upgrade with : sudo ./wizard.sh --upgrade "
echo "If you want to $ACTION anyway, please use '--force'."
echo ""
echo "Run : sudo ./wizard.sh --$ACTION --force"
exit 1
fi
fi
elif [[ $NEW_PATCH_VERSION -gt $CURRENT_PATCH_VERSION ]] ; then
log_warn "new version ($NEW_CS_VERSION) is a patch !"
if [[ $ACTION != "binupgrade" ]] ; then
if [[ ${FORCE_MODE} == "false" ]]; then
echo ""
echo "We recommend to upgrade binaries only : sudo ./wizard.sh --binupgrade "
echo "If you want to $ACTION anyway, please use '--force'."
echo ""
echo "Run : sudo ./wizard.sh --$ACTION --force"
exit 1
fi
fi
elif [[ $NEW_MINOR_VERSION -eq $CURRENT_MINOR_VERSION ]]; then
log_warn "new version ($NEW_CS_VERSION) is same as current version ($CURRENT_CS_VERSION) !"
if [[ ${FORCE_MODE} == "false" ]]; then
echo ""
echo "We recommend to $ACTION only if it's an higher version. "
echo "If it's an RC version (vX.X.X-rc) you can upgrade it using '--force'."
echo ""
echo "Run : sudo ./wizard.sh --$ACTION --force"
exit 1
fi
fi
}
# install crowdsec and cscli
install_crowdsec() {
mkdir -p "${CROWDSEC_DATA_DIR}"
(cd config && find patterns -type f -exec install -Dm 644 "{}" "${CROWDSEC_CONFIG_PATH}/{}" \; && cd ../) || exit
mkdir -p "${CROWDSEC_CONFIG_PATH}/acquis.d" || exit
mkdir -p "${CROWDSEC_CONFIG_PATH}/scenarios" || exit
mkdir -p "${CROWDSEC_CONFIG_PATH}/postoverflows" || exit
mkdir -p "${CROWDSEC_CONFIG_PATH}/collections" || exit
mkdir -p "${CROWDSEC_CONFIG_PATH}/patterns" || exit
mkdir -p "${CROWDSEC_CONFIG_PATH}/appsec-configs" || exit
mkdir -p "${CROWDSEC_CONFIG_PATH}/appsec-rules" || exit
mkdir -p "${CROWDSEC_CONFIG_PATH}/contexts" || exit
mkdir -p "${CROWDSEC_CONSOLE_DIR}" || exit
# tmp
mkdir -p /tmp/data
mkdir -p /etc/crowdsec/hub/
install -v -m 600 -D "./config/${CLIENT_SECRETS}" "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
install -v -m 600 -D "./config/${LAPI_SECRETS}" "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
## end tmp
install -v -m 600 -D ./config/config.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
install -v -m 644 -D ./config/dev.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
install -v -m 644 -D ./config/user.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
install -v -m 644 -D ./config/acquis.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
install -v -m 644 -D ./config/profiles.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
install -v -m 644 -D ./config/simulation.yaml "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
install -v -m 644 -D ./config/"${CONSOLE_FILE}" "${CROWDSEC_CONFIG_PATH}" 1> /dev/null || exit
install -v -m 644 -D ./config/context.yaml "${CROWDSEC_CONSOLE_DIR}" 1> /dev/null || exit
DATA=${CROWDSEC_DATA_DIR} CFG=${CROWDSEC_CONFIG_PATH} envsubst '$CFG $DATA' < ./config/user.yaml > ${CROWDSEC_CONFIG_PATH}"/user.yaml" || log_fatal "unable to generate user configuration file"
if [[ ${DOCKER_MODE} == "false" ]]; then
CFG=${CROWDSEC_CONFIG_PATH} BIN=${CROWDSEC_BIN_INSTALLED} envsubst '$CFG $BIN' < ./config/crowdsec.service > "${SYSTEMD_PATH_FILE}" || log_fatal "unable to crowdsec systemd file"
fi
install_bins
if [[ ${DOCKER_MODE} == "false" ]]; then
systemctl daemon-reload
fi
}
update_bins() {
log_info "Only upgrading binaries"
delete_bins
install_bins
log_info "Upgrade finished"
systemctl restart crowdsec || log_fatal "unable to restart crowdsec with systemctl"
}
update_full() {
if [[ ! -f "$CROWDSEC_BIN" ]]; then
log_err "Crowdsec binary '$CROWDSEC_BIN' not found. Please build it with 'make build'" && exit
fi
if [[ ! -f "$CSCLI_BIN" ]]; then
log_err "Cscli binary '$CSCLI_BIN' not found. Please build it with 'make build'" && exit
fi
log_info "Backing up existing configuration"
${CSCLI_BIN_INSTALLED} config backup ${BACKUP_DIR}
log_info "Saving default database content if exist"
if [[ -f "/var/lib/crowdsec/data/crowdsec.db" ]]; then
cp /var/lib/crowdsec/data/crowdsec.db ${BACKUP_DIR}/crowdsec.db
fi
log_info "Cleanup existing crowdsec configuration"
uninstall_crowdsec
log_info "Installing crowdsec"
install_crowdsec
log_info "Restoring configuration"
${CSCLI_BIN_INSTALLED} hub update
${CSCLI_BIN_INSTALLED} config restore ${BACKUP_DIR}
log_info "Restoring saved database if exist"
if [[ -f "${BACKUP_DIR}/crowdsec.db" ]]; then
cp ${BACKUP_DIR}/crowdsec.db /var/lib/crowdsec/data/crowdsec.db
fi
log_info "Finished, restarting"
systemctl restart crowdsec || log_fatal "Failed to restart crowdsec"
}
install_bins() {
log_dbg "Installing crowdsec binaries"
install -v -m 755 -D "${CROWDSEC_BIN}" "${CROWDSEC_BIN_INSTALLED}" 1> /dev/null || exit
install -v -m 755 -D "${CSCLI_BIN}" "${CSCLI_BIN_INSTALLED}" 1> /dev/null || exit
which systemctl && systemctl is-active --quiet crowdsec
if [ $? -eq 0 ]; then
systemctl stop crowdsec
fi
install_plugins
symlink_bins
}
symlink_bins() {
if grep -q "${BIN_INSTALL_PATH}" <<< $PATH; then
log_dbg "${BIN_INSTALL_PATH} found in PATH"
else
ln -s "${CSCLI_BIN_INSTALLED}" /usr/bin/cscli
ln -s "${CROWDSEC_BIN_INSTALLED}" /usr/bin/crowdsec
fi
}
delete_bins() {
log_info "Removing crowdsec binaries"
rm -f ${CROWDSEC_BIN_INSTALLED}
rm -f ${CSCLI_BIN_INSTALLED}
}
delete_plugins() {
rm -rf ${CROWDSEC_PLUGIN_DIR}
}
install_plugins(){
mkdir -p ${CROWDSEC_PLUGIN_DIR}
mkdir -p /etc/crowdsec/notifications
cp ${SLACK_PLUGIN_BINARY} ${CROWDSEC_PLUGIN_DIR}
cp ${SPLUNK_PLUGIN_BINARY} ${CROWDSEC_PLUGIN_DIR}
cp ${HTTP_PLUGIN_BINARY} ${CROWDSEC_PLUGIN_DIR}
cp ${EMAIL_PLUGIN_BINARY} ${CROWDSEC_PLUGIN_DIR}
cp ${SENTINEL_PLUGIN_BINARY} ${CROWDSEC_PLUGIN_DIR}
cp ${FILE_PLUGIN_BINARY} ${CROWDSEC_PLUGIN_DIR}
if [[ ${DOCKER_MODE} == "false" ]]; then
cp -n ${SLACK_PLUGIN_CONFIG} /etc/crowdsec/notifications/
cp -n ${SPLUNK_PLUGIN_CONFIG} /etc/crowdsec/notifications/
cp -n ${HTTP_PLUGIN_CONFIG} /etc/crowdsec/notifications/
cp -n ${EMAIL_PLUGIN_CONFIG} /etc/crowdsec/notifications/
cp -n ${SENTINEL_PLUGIN_CONFIG} /etc/crowdsec/notifications/
cp -n ${FILE_PLUGIN_CONFIG} /etc/crowdsec/notifications/
fi
}
check_running_bouncers() {
# when uninstalling, check if user still has bouncers
BOUNCERS_COUNT=$(${CSCLI_BIN} bouncers list -o=raw | tail -n +2 | wc -l)
if [[ ${BOUNCERS_COUNT} -gt 0 ]] ; then
if [[ ${FORCE_MODE} == "false" ]]; then
echo "WARNING : You have at least one bouncer registered (cscli bouncers list)."
echo "WARNING : Uninstalling crowdsec with a running bouncer will let it in an unpredictable state."
echo "WARNING : If you want to uninstall crowdsec, you should first uninstall the bouncers."
echo "Specify --force to bypass this restriction."
exit 1
fi;
fi
}
# uninstall crowdsec and cscli
uninstall_crowdsec() {
systemctl stop crowdsec.service 1>/dev/null
systemctl disable -q crowdsec.service 1>/dev/null
${CSCLI_BIN} dashboard remove -f -y >/dev/null
delete_bins
# tmp
rm -rf /tmp/data/
## end tmp
find /etc/crowdsec -maxdepth 1 -mindepth 1 | grep -v "bouncer" | xargs rm -rf || echo ""
rm -f ${CROWDSEC_LOG_FILE} || echo ""
rm -f ${LAPI_LOG_FILE} || echo ""
rm -f ${CROWDSEC_DB_PATH} || echo ""
rm -rf ${CROWDSEC_LIB_DIR} || echo ""
rm -rf ${CROWDSEC_USR_DIR} || echo ""
rm -f ${SYSTEMD_PATH_FILE} || echo ""
log_info "crowdsec successfully uninstalled"
}
function show_link {
echo ""
echo "Useful links to start with Crowdsec:"
echo ""
echo " - Documentation : https://doc.crowdsec.net/docs/getting_started/crowdsec_tour"
echo " - Crowdsec Hub : https://hub.crowdsec.net/ "
echo " - Open issues : https://github.com/crowdsecurity/crowdsec/issues"
echo ""
echo "Useful commands to start with Crowdsec:"
echo ""
echo " - sudo cscli metrics : https://doc.crowdsec.net/docs/observability/cscli"
echo " - sudo cscli decisions list : https://doc.crowdsec.net/docs/user_guides/decisions_mgmt"
echo " - sudo cscli hub list : https://doc.crowdsec.net/docs/user_guides/hub_mgmt"
echo ""
echo "Next step: visualize all your alerts and explore our community CTI : https://app.crowdsec.net"
echo ""
}
main() {
if [ "$1" == "install" ] || [ "$1" == "configure" ] || [ "$1" == "detect" ]; then
if [ "${SILENT}" == "false" ]; then
which whiptail > /dev/null
if [ $? -ne 0 ]; then
log_fatal "whiptail binary is needed to use the wizard in interactive mode, exiting ..."
fi
fi
which envsubst > /dev/null
if [ $? -ne 0 ]; then
log_fatal "envsubst binary is needed to use do a full install with the wizard, exiting ..."
fi
fi
if [[ "$1" == "binupgrade" ]];
then
if ! [ $(id -u) = 0 ]; then
log_err "Please run the wizard as root or with sudo"
exit 1
fi
check_cs_version
update_bins
return
fi
if [[ "$1" == "upgrade" ]];
then
if ! [ $(id -u) = 0 ]; then
log_err "Please run the wizard as root or with sudo"
exit 1
fi
check_cs_version
update_full
return
fi
if [[ "$1" == "configure" ]];
then
if ! [ $(id -u) = 0 ]; then
log_err "Please run the wizard as root or with sudo"
exit 1
fi
detect_services
${CSCLI_BIN_INSTALLED} hub update
install_collection
genacquisition
if ! skip_tmp_acquis; then
mv "${TMP_ACQUIS_FILE}" "${ACQUIS_TARGET}"
fi
return
fi
if [[ "$1" == "noop" ]];
then
return
fi
if [[ "$1" == "uninstall" ]];
then
if ! [ $(id -u) = 0 ]; then
log_err "Please run the wizard as root or with sudo"
exit 1
fi
check_running_bouncers
uninstall_crowdsec
return
fi
if [[ "$1" == "bininstall" ]];
then
if ! [ $(id -u) = 0 ]; then
log_err "Please run the wizard as root or with sudo"
exit 1
fi
log_info "checking existing crowdsec install"
detect_cs_install
log_info "installing crowdsec"
install_crowdsec
show_link
return
fi
if [[ "$1" == "install" ]];
then
if ! [ $(id -u) = 0 ]; then
log_err "Please run the wizard as root or with sudo"
exit 1
fi
log_info "checking if crowdsec is installed"
detect_cs_install
## Do make build before installing (as non--root) in order to have the binary and then install crowdsec as root
log_info "installing crowdsec"
install_crowdsec
log_dbg "configuring ${CSCLI_BIN_INSTALLED}"
${CSCLI_BIN_INSTALLED} hub update --error || (log_err "fail to update crowdsec hub. exiting" && exit 1)
# detect running services
detect_services
if ! [ ${#DETECTED_SERVICES[@]} -gt 0 ] ; then
log_err "No detected or selected services, stopping."
exit 1
fi;
# Generate acquisition file and move it to the right folder
genacquisition
if ! skip_tmp_acquis; then
mv "${TMP_ACQUIS_FILE}" "${ACQUIS_TARGET}"
fi
log_info "acquisition file path: ${ACQUIS_TARGET}"
# Install collections according to detected services
log_dbg "Installing needed collections ..."
install_collection
# install patterns/ folder
log_dbg "Installing patterns"
mkdir -p "${PATTERNS_PATH}"
cp "./${PATTERNS_FOLDER}/"* "${PATTERNS_PATH}/"
# api register
${CSCLI_BIN_INSTALLED} machines add --force "$(cat /etc/machine-id)" -a -f "${CROWDSEC_CONFIG_PATH}/${CLIENT_SECRETS}" || log_fatal "unable to add machine to the local API"
log_dbg "Crowdsec LAPI registered"
${CSCLI_BIN_INSTALLED} capi register --error || log_fatal "unable to register to the Central API"
systemctl enable -q crowdsec >/dev/null || log_fatal "unable to enable crowdsec"
systemctl start crowdsec >/dev/null || log_fatal "unable to start crowdsec"
log_info "enabling and starting crowdsec daemon"
show_link
return
fi
if [[ "$1" == "detect" ]];
then
if ! skip_tmp_acquis; then
rm -f "${TMP_ACQUIS_FILE}"
fi
detect_services
if [[ ${DETECTED_SERVICES} == "" ]] ; then
log_err "No detected or selected services, stopping."
exit
fi;
log_info "Found ${#DETECTED_SERVICES[@]} supported services running:"
genacquisition
cat "${TMP_ACQUIS_FILE}"
if ! skip_tmp_acquis; then
rm "${TMP_ACQUIS_FILE}"
fi
return
fi
}
usage() {
echo "Usage:"
echo " ./wizard.sh -h Display this help message."
echo " ./wizard.sh -d|--detect Detect running services and associated logs file"
echo " ./wizard.sh -i|--install Assisted installation of crowdsec/cscli and collections"
echo " ./wizard.sh --bininstall Install binaries and empty config, no wizard."
echo " ./wizard.sh --uninstall Uninstall crowdsec/cscli"
echo " ./wizard.sh --binupgrade Upgrade crowdsec/cscli binaries"
echo " ./wizard.sh --upgrade Perform a full upgrade and try to migrate configs"
echo " ./wizard.sh --unattended Install in unattended mode, no question will be asked and defaults will be followed"
echo " ./wizard.sh --docker-mode Will install crowdsec without systemd and generate random machine-id"
echo " ./wizard.sh -n|--noop Do nothing"
exit 0
}
if [[ $# -eq 0 ]]; then
usage
fi
while [[ $# -gt 0 ]]
do
key="${1}"
case ${key} in
--uninstall)
ACTION="uninstall"
shift # past argument
;;
--binupgrade)
ACTION="binupgrade"
shift # past argument
;;
--upgrade)
ACTION="upgrade"
shift # past argument
;;
-i|--install)
ACTION="install"
shift # past argument
;;
--bininstall)
ACTION="bininstall"
shift # past argument
;;
--docker-mode)
DOCKER_MODE="true"
ACTION="bininstall"
shift # past argument
;;
-c|--configure)
ACTION="configure"
shift # past argument
;;
-d|--detect)
ACTION="detect"
shift # past argument
;;
-n|--noop)
ACTION="noop"
shift # past argument
;;
--unattended)
SILENT="true"
ACTION="install"
shift
;;
-f|--force)
FORCE_MODE="true"
shift
;;
-v|--verbose)
DEBUG_MODE="true"
shift
;;
-h|--help)
usage
exit 0
;;
*) # unknown option
log_err "Unknown argument ${key}."
usage
exit 1
;;
esac
done
main ${ACTION}