Gitea Actions Runner Test / test-job (push) Successful in 1s
CI / check (push) Successful in 29s
CI / tests-integration (push) Successful in 1m46s
CI / tests-unit (push) Successful in 1m59s
CI / tests-ui (push) Successful in 2m53s
CI / preflight (push) Skipped
CI / deploy (push) Failing after 1m54s
154 lines
5.6 KiB
Bash
154 lines
5.6 KiB
Bash
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$DIR"
|
|
ENV_FILE="$DIR/.env"
|
|
COMPOSE_FILE="deployment/crowdsec/compose.crowdsec.yml"
|
|
PROJECT_NAME="epicnext-crowdsec"
|
|
CONTAINER_NAME="epicnext-crowdsec"
|
|
DEFAULT_PORT="18080"
|
|
|
|
mode="${1:-enable}"
|
|
case "$mode" in
|
|
enable|--enable) ;;
|
|
status|--status) ;;
|
|
disable|--disable) ;;
|
|
blocklists|blocklists-install-cron|blocklists-uninstall-cron) ;;
|
|
*) echo "Usage: bash cms security [enable|status|disable|blocklists|blocklists-install-cron|blocklists-uninstall-cron]" >&2; exit 1 ;;
|
|
esac
|
|
|
|
umask 077
|
|
fail() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
|
for command in docker flock; do command -v "$command" >/dev/null || fail "Required command: $command"; done
|
|
docker info >/dev/null 2>&1 || fail "Docker is not reachable."
|
|
docker compose version >/dev/null 2>&1 || fail "Docker Compose plugin required."
|
|
exec 9>"$DIR/.deploy.lock"
|
|
flock -w 30 9 || fail "Another installation or update is running."
|
|
[[ -f "$ENV_FILE" ]] || fail "Create .env first (bash cms install)."
|
|
|
|
case "$mode" in
|
|
blocklists) exec bash "$DIR/scripts/blocklists-sync.sh" sync "${2:-}" ;;
|
|
blocklists-install-cron) exec bash "$DIR/scripts/blocklists-sync.sh" install-cron ;;
|
|
blocklists-uninstall-cron) exec bash "$DIR/scripts/blocklists-sync.sh" uninstall-cron ;;
|
|
esac
|
|
|
|
env_get() {
|
|
local key="$1" line
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
case "$line" in
|
|
"$key="*) line="${line#*=}"; line="${line%\"}"; line="${line#\"}"; printf '%s' "$line"; return 0 ;;
|
|
esac
|
|
done < "$ENV_FILE"
|
|
return 1
|
|
}
|
|
|
|
env_set() {
|
|
local key="$1" value="$2" tmp
|
|
tmp="$(mktemp "$DIR/.env.crowdsec.XXXXXX")"
|
|
if awk -v k="$key" -v v="$value" 'BEGIN{FS=OFS="=";done=0} { if ($1==k) { print k "=" v; done=1 } else print } END { if (!done) print k "=" v }' "$ENV_FILE" > "$tmp"; then
|
|
chmod 600 "$tmp"
|
|
mv -f -- "$tmp" "$ENV_FILE"
|
|
else
|
|
rm -f -- "$tmp"
|
|
fail "Could not update .env"
|
|
fi
|
|
}
|
|
|
|
compose_cmd() {
|
|
docker compose --project-name "$PROJECT_NAME" --env-file "$ENV_FILE" -f "$COMPOSE_FILE" --profile security "$@"
|
|
}
|
|
|
|
health_probe() {
|
|
local url="$1"
|
|
if command -v curl >/dev/null 2>&1; then
|
|
curl -fsS --max-time 3 "$url" >/dev/null 2>&1
|
|
else
|
|
compose_cmd exec -T crowdsec wget -q -O - "$url" >/dev/null 2>&1
|
|
fi
|
|
}
|
|
|
|
container_running() {
|
|
[[ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null || true)" = true ]]
|
|
}
|
|
|
|
if [[ "$mode" = disable || "$mode" = --disable ]]; then
|
|
set +e
|
|
compose_cmd stop crowdsec
|
|
rc=$?
|
|
set -e
|
|
[[ $rc -eq 0 ]] || printf 'CrowdSec engine was not running or could not be stopped.\n'
|
|
env_set CROWDSEC_LOCAL_ENABLED false
|
|
printf 'CrowdSec local stack disabled. The engine container is stopped; volumes and .env key were kept.\n'
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$mode" = status || "$mode" = --status ]]; then
|
|
enabled=no
|
|
[[ "$(env_get CROWDSEC_LOCAL_ENABLED 2>/dev/null || true)" = true ]] && enabled=yes
|
|
port="$(env_get CROWDSEC_LAPI_PORT 2>/dev/null || true)"
|
|
[[ -z "$port" ]] && port="$DEFAULT_PORT"
|
|
printf 'CROWDSEC_LOCAL_ENABLED=%s\n' "$enabled"
|
|
if container_running; then
|
|
printf 'Engine: running\n'
|
|
if health_probe "http://127.0.0.1:$port/health"; then
|
|
printf 'LAPI health: OK (127.0.0.1:%s)\n' "$port"
|
|
else
|
|
printf 'LAPI health: UNREACHABLE (127.0.0.1:%s)\n' "$port"
|
|
fi
|
|
else
|
|
printf 'Engine: not running\n'
|
|
printf 'Start with: bash cms security\n'
|
|
fi
|
|
exit 0
|
|
fi
|
|
|
|
port="$(env_get CROWDSEC_LAPI_PORT 2>/dev/null || true)"
|
|
[[ -n "$port" ]] || port="$DEFAULT_PORT"
|
|
[[ "$port" =~ ^[0-9]{1,5}$ ]] || fail "CROWDSEC_LAPI_PORT must be a port number."
|
|
if (( port < 1024 || port > 65535 )); then
|
|
fail "CROWDSEC_LAPI_PORT must be within 1024-65535."
|
|
fi
|
|
key="$(env_get CROWDSEC_LAPI_API_KEY 2>/dev/null || true)"
|
|
[[ -n "$key" ]] || key="$(od -An -N32 -tx1 /dev/urandom | tr -d ' \n')"
|
|
url="$(env_get CROWDSEC_LAPI_URL 2>/dev/null || true)"
|
|
[[ -n "$url" ]] || url="http://127.0.0.1:$port"
|
|
log_dir="${CROWDSEC_NGINX_LOG_DIR:-$(env_get CROWDSEC_NGINX_LOG_DIR 2>/dev/null || true)}"
|
|
[[ -n "$log_dir" ]] || log_dir="/var/log/nginx"
|
|
|
|
if ! container_running && command -v ss >/dev/null 2>&1; then
|
|
if ss -ltn "( sport = :$port )" 2>/dev/null | grep -q LISTEN; then
|
|
fail "Port $port is already in use. Set CROWDSEC_LAPI_PORT (and CROWDSEC_LAPI_URL) in .env to a free port and re-run."
|
|
fi
|
|
fi
|
|
|
|
if [[ ! -r "$log_dir/access.log" ]]; then
|
|
printf 'Warning: %s/access.log is not readable. The engine will run but has no detections until an access log is available.\n' "$log_dir"
|
|
fi
|
|
|
|
env_set CROWDSEC_LOCAL_ENABLED true
|
|
env_set CROWDSEC_LAPI_URL "$url"
|
|
env_set CROWDSEC_LAPI_PORT "$port"
|
|
env_set CROWDSEC_LAPI_API_KEY "$key"
|
|
env_set CROWDSEC_NGINX_LOG_DIR "$log_dir"
|
|
|
|
compose_cmd config --quiet || fail "CrowdSec Compose configuration is invalid; fix CROWDSEC_* settings in .env."
|
|
set +e
|
|
compose_cmd up -d --wait crowdsec
|
|
rc=$?
|
|
set -e
|
|
if [[ $rc -ne 0 ]]; then
|
|
compose_cmd up -d crowdsec
|
|
fi
|
|
|
|
attempt=0
|
|
while ! health_probe "http://127.0.0.1:$port/health"; do
|
|
attempt=$((attempt + 1))
|
|
[[ $attempt -lt 30 ]] || fail "CrowdSec LAPI did not become healthy on port $port."
|
|
sleep 2
|
|
done
|
|
|
|
printf 'CrowdSec engine running in LAPI-only mode on 127.0.0.1:%s (container %s).\n' "$port" "$CONTAINER_NAME"
|
|
compose_cmd exec -T crowdsec cscli bouncers list >/dev/null 2>&1 \
|
|
&& printf 'Bouncer "cms" was registered against the local LAPI.\n' \
|
|
|| printf 'Warning: could not list bouncers. Diagnose with: docker compose exec -T %s cscli bouncers list\n' "$CONTAINER_NAME"
|
|
printf 'Restart the CMS container (or run your next deployment) so it loads the new bouncer env. For a clone: bash cms update --skip-pull\n' |