#!/usr/bin/env bash # SOC onboarding - Linux # Installs Wazuh agent (enrolled to the SOC) + CrowdSec, and wires CrowdSec alerts into Wazuh. # Usage: bash soc-install-linux.sh -g -p [-n ] [--no-crowdsec] set -euo pipefail MANAGER="soc.cdnfee.com" WAZUH_VERSION="4.14.10-1" GROUP="" PASS="" NAME="$(hostname -f 2>/dev/null || hostname)" CROWDSEC=1 log() { printf '\033[1;34m[SOC]\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m[SOC] WARNING:\033[0m %s\n' "$*" >&2; } die() { printf '\033[1;31m[SOC] ERROR:\033[0m %s\n' "$*" >&2; exit 1; } while [ $# -gt 0 ]; do case "$1" in -g) GROUP="${2:-}"; shift 2 ;; -p) PASS="${2:-}"; shift 2 ;; -n) NAME="${2:-}"; shift 2 ;; --no-crowdsec) CROWDSEC=0; shift ;; -h|--help) sed -n '2,5p' "$0"; exit 0 ;; *) die "unknown option: $1" ;; esac done [ "$(id -u)" -eq 0 ] || die "run as root" [ -n "$GROUP" ] && [ -n "$PASS" ] || die "both -g and -p are required" [[ "$GROUP" =~ ^c-[a-z0-9-]+$ ]] || die "invalid enrollment code" [ -d /var/ossec ] && die "/var/ossec already exists (Wazuh agent or manager already installed) - aborting" if command -v apt-get >/dev/null; then PM=apt elif command -v dnf >/dev/null; then PM=dnf elif command -v yum >/dev/null; then PM=yum else die "unsupported distribution (need apt, dnf or yum)"; fi # ---------- connectivity ---------- log "Checking connectivity to $MANAGER ..." for port in 1514 1515; do timeout 5 bash -c "/dev/null || die "cannot reach $MANAGER:$port (check outbound firewall)" done # ---------- Wazuh agent ---------- log "Installing Wazuh agent $WAZUH_VERSION ..." export WAZUH_MANAGER="$MANAGER" WAZUH_REGISTRATION_SERVER="$MANAGER" \ WAZUH_REGISTRATION_PASSWORD="$PASS" WAZUH_AGENT_GROUP="$GROUP" WAZUH_AGENT_NAME="$NAME" if [ "$PM" = apt ]; then export DEBIAN_FRONTEND=noninteractive apt-get update -qq && apt-get install -y -qq curl gnupg apt-transport-https >/dev/null curl -fsSL https://packages.wazuh.com/key/GPG-KEY-WAZUH | gpg --dearmor --yes -o /usr/share/keyrings/wazuh.gpg chmod 644 /usr/share/keyrings/wazuh.gpg echo "deb [signed-by=/usr/share/keyrings/wazuh.gpg] https://packages.wazuh.com/4.x/apt/ stable main" > /etc/apt/sources.list.d/wazuh.list apt-get update -qq apt-get install -y -qq "wazuh-agent=$WAZUH_VERSION" >/dev/null apt-mark hold wazuh-agent >/dev/null else rpm --import https://packages.wazuh.com/key/GPG-KEY-WAZUH cat > /etc/yum.repos.d/wazuh.repo <<'EOF' [wazuh] gpgcheck=1 gpgkey=https://packages.wazuh.com/key/GPG-KEY-WAZUH enabled=1 name=Wazuh repository baseurl=https://packages.wazuh.com/4.x/yum/ protect=1 EOF $PM install -y -q "wazuh-agent-${WAZUH_VERSION}" sed -i 's/^enabled=1/enabled=0/' /etc/yum.repos.d/wazuh.repo # avoid uncontrolled upgrades fi unset WAZUH_REGISTRATION_PASSWORD systemctl daemon-reload systemctl enable --now wazuh-agent >/dev/null 2>&1 log "Waiting for the agent to connect ..." ok=0 for _ in $(seq 1 30); do if grep -q "Connected to the server" /var/ossec/logs/ossec.log 2>/dev/null; then ok=1; break; fi if grep -qE "Invalid password|Invalid group|Duplicate agent name" /var/ossec/logs/ossec.log 2>/dev/null; then break; fi sleep 2 done if [ $ok -eq 1 ]; then log "Wazuh agent connected." else grep -E "ERROR|authd|Invalid" /var/ossec/logs/ossec.log | tail -5 >&2 || true die "Wazuh agent did not connect - check the enrollment code/password above" fi # ---------- CrowdSec ---------- if [ $CROWDSEC -eq 1 ]; then if systemctl is-active -q fail2ban 2>/dev/null; then warn "fail2ban is running - it overlaps with CrowdSec; consider disabling it after verifying CrowdSec." fi log "Installing CrowdSec ..." curl -fsSL https://install.crowdsec.net | sh >/dev/null if [ "$PM" = apt ]; then apt-get install -y -qq crowdsec >/dev/null; else $PM install -y -q crowdsec; fi if command -v nft >/dev/null; then BOUNCER=crowdsec-firewall-bouncer-nftables; else BOUNCER=crowdsec-firewall-bouncer-iptables; fi if [ "$PM" = apt ]; then apt-get install -y -qq "$BOUNCER" >/dev/null; else $PM install -y -q "$BOUNCER"; fi # web server collections command -v apache2 >/dev/null || command -v httpd >/dev/null && cscli collections install crowdsecurity/apache2 >/dev/null 2>&1 || true command -v nginx >/dev/null && cscli collections install crowdsecurity/nginx >/dev/null 2>&1 || true command -v caddy >/dev/null && cscli collections install crowdsecurity/caddy >/dev/null 2>&1 || true # send CrowdSec alerts to a JSON file read by the Wazuh agent install -d -o nobody -g wazuh -m 2750 /var/log/crowdsec-alerts cat > /etc/crowdsec/notifications/file.yaml <<'EOF' type: file name: file_default log_level: info format: | {{range . -}} {"crowdsec":{"scenario":{{.Scenario | toJson}},"source_ip":{{.Source.IP | toJson}},"source_range":{{.Source.Range | toJson}},"country":{{.Source.Cn | toJson}},"as_name":{{.Source.AsName | toJson}},"events_count":{{.EventsCount}},"machine":{{.MachineID | toJson}},"decision":"{{range .Decisions}}{{.Type}} {{.Duration}}{{end}}","message":{{.Message | toJson}}}} {{ end -}} log_path: "/var/log/crowdsec-alerts/alerts.json" rotate: enabled: true max_size: 100 max_files: 5 max_age: 30 compress: true EOF if ! grep -q "file_default" /etc/crowdsec/profiles.yaml; then cp /etc/crowdsec/profiles.yaml /etc/crowdsec/profiles.yaml.orig sed -i 's/^on_success: break/notifications:\n - file_default\non_success: break/' /etc/crowdsec/profiles.yaml fi systemctl restart crowdsec sleep 3 systemctl is-active -q crowdsec || die "CrowdSec failed to start (journalctl -u crowdsec)" log "CrowdSec running with bouncer $BOUNCER." fi # ---------- hints ---------- if command -v caddy >/dev/null && [ ! -f /var/log/caddy/access.log ]; then warn "Caddy detected: add 'log { output file /var/log/caddy/access.log }' to your Caddyfile so the SOC can read web logs." fi log "Done. Agent '$NAME' enrolled in SOC ($GROUP)."