aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorheqnx <root@heqnx.com>2025-05-13 12:36:45 +0300
committerheqnx <root@heqnx.com>2025-05-13 12:36:45 +0300
commit5c23f03b29cad13844eac9b473801c99ff557279 (patch)
tree914bbb629892aaf66c9244076ba0aca323a671f4
parentdc6d6e028489996d4bfd12b28147b86e5cf0551e (diff)
downloadgists-5c23f03b29cad13844eac9b473801c99ff557279.tar.gz
gists-5c23f03b29cad13844eac9b473801c99ff557279.zip
added setup-command-canaries.sh
-rw-r--r--setup-command-canaries.sh138
1 files changed, 138 insertions, 0 deletions
diff --git a/setup-command-canaries.sh b/setup-command-canaries.sh
new file mode 100644
index 0000000..60324bd
--- /dev/null
+++ b/setup-command-canaries.sh
@@ -0,0 +1,138 @@
+#!/bin/bash
+set -e
+
+if ! command -v curl &>/dev/null; then
+ printf "%s\n" "[err] curl not found"
+ exit 1
+fi
+
+function usage() {
+ printf "%s\n" \
+ "set up command canaries for early intrusion detection via webhook notifications" \
+ "usage: $(basename ${0}) <args>" \
+ "" \
+ "options" \
+ "-c <cmd> commands to monitor; repeatable, e.g., -c whoami -c id" \
+ "-s <service> notification service: discord|slack" \
+ "-u <url> webhook url" \
+ "-m <method> http method, default POST" \
+ "-x <header> webhook headers; repeatable, e.g., -x 'key1: value1' -x 'key2: value2'" \
+ "-t <channel> channel or destination id for notifications" \
+ "-h print this help message and exit" \
+ "" \
+ "examples" \
+ "discord" \
+ "$(basename ${0}) -s discord -c whoami -c id -u https://discord.com/api/webhooks/xxx" \
+ "" \
+ "slack" \
+ "$(basename ${0}) -s slack -c whoami -c id -u https://slack.com/api/chat.postmessage -t mychannel -x 'authorization: bearer xoxb-a-b-c'" \
+ "" \
+ "output" \
+ "generates canaries.txt with shell functions to append to /etc/bash.bashrc or ~/.bashrc."
+ exit 1
+}
+
+function validate_url() {
+ local url="${1}"
+ if ! [[ "${url}" =~ ^https:// ]]; then
+ printf "%s\n" "[err] invalid url: ${url} (must be https)"
+ exit 1
+ fi
+}
+
+function validate_method() {
+ local method="${1}"
+ if ! [[ "${method}" =~ ^(GET|POST|PUT)$ ]]; then
+ printf "[err] invalid http method: ${method} (use GET, POST, or PUT)"
+ exit 1
+ fi
+}
+
+function validate_command() {
+ local cmd="${1}"
+ if ! command -v "${cmd}" &>/dev/null; then
+ printf "[wrn] command '${cmd}' not found in path"
+ fi
+}
+
+output_file="canaries.txt"
+method="POST"
+commands=()
+webhook_headers=()
+channel=""
+
+while getopts "c:s:u:m:x:t:h" opts; do
+ case "${opts}" in
+ c) commands+=("${OPTARG}");;
+ s) service="${OPTARG}";;
+ u) webhook_url="${OPTARG}";;
+ m) method="${OPTARG}";;
+ x) webhook_headers+=("${OPTARG}");;
+ t) channel="${OPTARG}";;
+ h) usage;;
+ *) usage;;
+ esac
+done
+
+if test ${#commands[@]} -eq 0; then
+ printf "%s\n" "[err] at least one command (-c) is required"
+ usage
+fi
+if ! test "${webhook_url}" && ! test "${service}"; then
+ printf "%s\n" "[err] webhook url (-u) or service (-s) is required"
+ usage
+fi
+
+validate_method "${method}"
+for cmd in "${commands[@]}"; do
+ validate_command "${cmd}"
+done
+
+test "${webhook_url}" && validate_url "${webhook_url}"
+
+case "${service}" in
+ discord)
+ webhook_url="${webhook_url:-}"
+ webhook_headers+=("Content-Type: application/json")
+ webhook_data="{\"username\":\"notifications-bot\", \"content\":\"**Command Canary Triggered**\nHostname: \${HOSTNAME}\nCommand: \${FUNCNAME[0]}\nTime: \$(date -u --iso-8601=seconds)\"}"
+ ;;
+
+ slack)
+ webhook_headers+=("Content-Type: application/json")
+ ! test "${channel}" || { printf "%s\n" "[err] slack requires a channel (-t)"; exit 1; }
+ webhook_data="{\"text\":\"**Command Canary Triggered**\\nHostname: \${HOSTNAME}\\nCommand: \${FUNCNAME[0]}\\nTime: \$(date -u --iso-8601=seconds)\",\"channel\":\"${channel}\"}"
+ ;;
+ *)
+ usage
+ ;;
+esac
+
+curl_headers=""
+for header in "${webhook_headers[@]}"; do
+ curl_headers+="-H '${header}' "
+done
+
+>"${output_file}"
+
+for cmd in "${commands[@]}"; do
+ sanitized_data=$(printf "%s" "${webhook_data}" | sed -e 's/"/\\"/g')
+ cat >> "${output_file}" << EOF
+function ${cmd}() {
+ (
+ $(which curl) -sSkL \\
+ -X ${method} \\
+ ${curl_headers} \\
+ --data "${sanitized_data}" \\
+ ${webhook_url} \\
+ -o /dev/null 2>&1 >/dev/null &
+ )
+ command ${cmd} "\${@}"
+}
+EOF
+done
+
+printf "%s\n" \
+ "[inf] successfully created ${output_file}" \
+ "[inf] to enable canaries, append the contents to:" \
+ " - System-wide: /etc/bash.bashrc" \
+ " - User-specific: ~/.bashrc"