#!/bin/sh
# =============================================================================
# SigmaSRC Platform Agent -- Quick Install (Linux & macOS)
#
# One-liner (interactive: the package/agent prompts for the settings):
#
#   curl -fsSL https://dl.sigmasrc.com/install.sh | sudo sh
#
# Unattended (pass the central server, etc. -- no prompts):
#
#   curl -fsSL https://dl.sigmasrc.com/install.sh | sudo SIGMA_SERVER=esp.example.com sh
#
#   ...or with flags:
#
#   sudo sh -c "$(curl -fsSL https://dl.sigmasrc.com/install.sh)" \
#       -- --server esp.example.com --enforce --enterprise
#
# On Linux this REGISTERS the signed SigmaSRC apt/yum repository (so the agent
# stays current via `apt upgrade` / `dnf update`) and then installs it. On macOS
# it downloads + runs the signed .pkg (macOS has no package repo). Enforcement
# defaults OFF so an install never silently black-holes the host's network.
# =============================================================================
set -eu

# ----------------------------------------------------------------------------
# Distribution endpoints. Override any with the matching SIGMA_* env var.
# ----------------------------------------------------------------------------
VERSION="${SIGMA_VERSION:-1.6.0}"
DL_HOST="${SIGMA_DL_HOST:-dl.sigmasrc.com}"     # downloads: macOS .pkg, key, tarballs
APT_HOST="${SIGMA_APT_HOST:-apt.sigmasrc.com}"  # Debian/Ubuntu repo
YUM_HOST="${SIGMA_YUM_HOST:-yum.sigmasrc.com}"  # RHEL/Alma/Amazon repo
BASE_URL="${SIGMA_BASE_URL:-https://${DL_HOST}}"
KEY_URL="${SIGMA_KEY_URL:-${BASE_URL}/sigma.gpg}"                       # armored, for rpm --import
APT_KEY_URL="${SIGMA_APT_KEY_URL:-${BASE_URL}/sigma-archive-keyring.gpg}" # binary keyring, for apt signed-by

# ----------------------------------------------------------------------------
# Settings (env defaults; flags below override). Leave SERVER empty to let the
# package/agent prompt interactively.
# ----------------------------------------------------------------------------
SERVER="${SIGMA_SERVER:-}"
ENFORCE="${SIGMA_ENFORCE:-false}"          # true|false  -- poliwall enforcement
ENTERPRISE="${SIGMA_ENTERPRISE:-no}"       # yes|no      -- enterprise install

# Integrity for the macOS direct download: SHA256_EXPECTED (if set) is matched
# against the .pkg, else a "<file>.sha256" sidecar is fetched. REQUIRE_CHECKSUM=1
# makes a *missing* checksum fatal (default: warn). The apt/yum repos verify
# integrity via their GPG-signed metadata, so these only apply to macOS.
SHA256_EXPECTED="${SIGMA_SHA256:-}"
REQUIRE_CHECKSUM="${SIGMA_REQUIRE_CHECKSUM:-0}"

log()  { printf 'sigma-install: %s\n' "$*"; }
err()  { printf 'sigma-install: error: %s\n' "$*" >&2; exit 1; }

bool() { # normalise to true/false
    case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
        y|yes|true|1|on|enforce|enterprise) echo true ;;
        *) echo false ;;
    esac
}

usage() {
    sed -n '2,26p' "$0" 2>/dev/null || true
    cat <<'EOF'
Flags:
  --server H        ESP central server this agent reports to
  --enforce         enable poliwall packet-filter enforcement (default: off)
  --enterprise      enterprise install (enterprise-only compliance rules)
  --sha256 HEX      expected SHA-256 of the macOS .pkg (else a .sha256 sidecar)
  --require-checksum  fail if no checksum is available for a direct download
  --key-url U       override the signing-key URL
  --version V       override package version (macOS .pkg)
  -h, --help        show this help
EOF
    exit 0
}

# ----------------------------------------------------------------------------
# Parse flags ("--" args after the script name).
# ----------------------------------------------------------------------------
while [ $# -gt 0 ]; do
    case "$1" in
        --server)     SERVER="${2:-}"; shift ;;
        --enforce)    ENFORCE=true ;;
        --no-enforce) ENFORCE=false ;;
        --enterprise) ENTERPRISE=yes ;;
        --sha256)     SHA256_EXPECTED="${2:-}"; shift ;;
        --require-checksum) REQUIRE_CHECKSUM=1 ;;
        --key-url)    KEY_URL="${2:-}"; shift ;;
        --version)    VERSION="${2:-}"; shift ;;
        -h|--help)    usage ;;
        *) err "unknown argument '$1' (try --help)" ;;
    esac
    shift
done

ENFORCE="$(bool "$ENFORCE")"
[ "$(bool "$ENTERPRISE")" = true ] && ENTERPRISE=enterprise || ENTERPRISE=

# ----------------------------------------------------------------------------
# Must be root.
# ----------------------------------------------------------------------------
if [ "$(id -u)" -ne 0 ]; then
    err "must run as root. Re-run with sudo, e.g.:
       sudo sh -c \"\$(curl -fsSL https://${DL_HOST}/install.sh)\""
fi

# ----------------------------------------------------------------------------
# Download helpers + temp workspace.
# ----------------------------------------------------------------------------
TMPDIR_DL="$(mktemp -d "${TMPDIR:-/tmp}/sigma-install.XXXXXX")"
cleanup() { rm -rf "$TMPDIR_DL"; }
trap cleanup EXIT INT TERM

fetch() { # fetch <url> <dest>
    log "downloading $1"
    if command -v curl >/dev/null 2>&1; then
        curl -fSL --proto '=https' -o "$2" "$1" \
            || err "download failed: $1"
    elif command -v wget >/dev/null 2>&1; then
        wget -O "$2" "$1" || err "download failed: $1"
    else
        err "need curl or wget to download"
    fi
}

fetch_opt() { # fetch_opt <url> <dest> -- quiet, non-fatal (returns 1 on failure)
    if command -v curl >/dev/null 2>&1; then
        curl -fsSL --proto '=https' -o "$2" "$1" 2>/dev/null
    elif command -v wget >/dev/null 2>&1; then
        wget -q -O "$2" "$1" 2>/dev/null
    else
        return 1
    fi
}

sha256_of() { # sha256_of <file> -> lowercase hex on stdout
    if command -v sha256sum >/dev/null 2>&1; then
        sha256sum "$1" | awk '{print $1}'
    elif command -v shasum >/dev/null 2>&1; then
        shasum -a 256 "$1" | awk '{print $1}'
    elif command -v openssl >/dev/null 2>&1; then
        openssl dgst -sha256 "$1" | awk '{print $NF}'
    else
        return 1
    fi
}

verify_checksum() { # verify_checksum <file> <source-url>
    file="$1"; url="$2"
    expected="$SHA256_EXPECTED"

    # Fall back to a "<file>.sha256" sidecar served next to the file.
    if [ -z "$expected" ]; then
        side="${file}.sha256"
        if fetch_opt "${url}.sha256" "$side"; then
            expected="$(awk 'NR==1{print $1}' "$side")"
        fi
    fi

    if [ -z "$expected" ]; then
        if [ "$REQUIRE_CHECKSUM" = 1 ]; then
            err "no checksum available for $(basename "$file") (pass --sha256 or publish a .sha256 sidecar)"
        fi
        log "warning: no checksum available -- skipping integrity verification"
        return 0
    fi

    actual="$(sha256_of "$file")" \
        || err "cannot compute SHA-256 (need sha256sum, shasum, or openssl)"

    expected="$(printf '%s' "$expected" | tr 'A-F' 'a-f')"
    actual="$(printf '%s' "$actual" | tr 'A-F' 'a-f')"
    if [ "$expected" != "$actual" ]; then
        rm -f "$file"
        err "checksum mismatch for $(basename "$file")
       expected: $expected
       actual:   $actual"
    fi
    log "checksum OK (sha256: $actual)"
}

ARCH="$(uname -m)"
case "$ARCH" in
    x86_64|amd64) ;;
    *) err "unsupported architecture '$ARCH' (only x86_64 packages are published today)" ;;
esac

OS="$(uname -s)"

# distro_tag: ID + VERSION_ID -> repo tag (ubuntu2204 / el9 / amzn2023).
distro_tag() {
    [ -r /etc/os-release ] || { echo ""; return; }
    # shellcheck disable=SC1091
    . /etc/os-release
    case "${ID:-}" in
        ubuntu) echo "ubuntu${VERSION_ID%%.*}$(printf '%s' "${VERSION_ID#*.}" | cut -c1-2)" ;;
        rhel|centos|rocky|almalinux|ol) echo "el${VERSION_ID%%.*}" ;;
        amzn)   echo "amzn${VERSION_ID%%.*}" ;;
        *)      echo "" ;;
    esac
}

# =============================================================================
# macOS -- download + run the signed .pkg (no repo on macOS).
# =============================================================================
install_macos() {
    pkg="SigmaAgent-${VERSION}.pkg"
    url="${BASE_URL}/mac/${pkg}"
    dest="${TMPDIR_DL}/${pkg}"
    fetch "$url" "$dest"
    verify_checksum "$dest" "$url"

    if [ -n "$SERVER" ]; then
        log "installing (unattended) server=$SERVER enforce=$ENFORCE enterprise=${ENTERPRISE:-no}"
        ESI_NOPROMPT=1 \
        ESI_SERVER="$SERVER" \
        ESI_PWENFORCE="$ENFORCE" \
        ESI_ENTERPRISE="$ENTERPRISE" \
            installer -pkg "$dest" -target /
    else
        log "installing (the installer will prompt for settings)"
        installer -pkg "$dest" -target /
    fi
}

# =============================================================================
# Debian/Ubuntu -- register the apt repo + signing key, then install.
# =============================================================================
install_deb() {
    suite="$(distro_tag)"
    case "$suite" in
        ubuntu2204|ubuntu2404|ubuntu2604) ;;
        *) err "no apt repository for this distro/release.
       Published suites: ubuntu2204, ubuntu2404, ubuntu2604 (Ubuntu 22.04/24.04/26.04).
       Detected tag: '${suite:-unknown}'." ;;
    esac

    # Install the binary signing keyring referenced by signed-by (no client gpg needed).
    keyring="/usr/share/keyrings/sigma-archive-keyring.gpg"
    fetch "$APT_KEY_URL" "$keyring"
    chmod 0644 "$keyring"

    printf 'deb [signed-by=%s arch=amd64] https://%s %s main\n' \
        "$keyring" "$APT_HOST" "$suite" > /etc/apt/sources.list.d/sigma.list
    log "registered apt repo: https://${APT_HOST} ${suite} main"

    # Preseed the agent's three install questions when a server was supplied.
    if [ -n "$SERVER" ] && command -v debconf-set-selections >/dev/null 2>&1; then
        log "preseeding: server=$SERVER enforce=$ENFORCE enterprise=${ENTERPRISE:-no}"
        { printf 'sigmaagent sigmaagent/server string %s\n' "$SERVER"
          printf 'sigmaagent sigmaagent/enforce boolean %s\n' "$ENFORCE"
          [ -n "$ENTERPRISE" ] && printf 'sigmaagent sigmaagent/enterprise boolean true\n' \
                               || printf 'sigmaagent sigmaagent/enterprise boolean false\n'
        } | debconf-set-selections
        export DEBIAN_FRONTEND=noninteractive
    fi

    apt-get update
    apt-get install -y sigmaagent
}

# =============================================================================
# RHEL/Alma/Amazon -- register the yum repo + signing key, then install.
# =============================================================================
install_rpm() {
    tag="$(distro_tag)"
    case "$tag" in
        el9|amzn2023) ;;
        *) err "no yum repository for this distro/release.
       Published: el9 (RHEL/Alma/Rocky 9), amzn2023 (Amazon Linux 2023).
       Detected tag: '${tag:-unknown}'." ;;
    esac

    rpm --import "$KEY_URL" || err "failed to import signing key from $KEY_URL"

    cat > /etc/yum.repos.d/sigma.repo <<EOF
[sigma]
name=SigmaSRC Platform Agent
baseurl=https://${YUM_HOST}/${tag}/\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=${KEY_URL}
EOF
    log "registered yum repo: https://${YUM_HOST}/${tag}/\$basearch"

    # The RPM %post runs `sigma-agent-setup --install`, which honours these env
    # vars when set, otherwise prompts on a TTY.
    if [ -n "$SERVER" ]; then
        export CSERVER="$SERVER" PW_ENFORCE="$ENFORCE" ESI_ENTERPRISE="$ENTERPRISE"
        log "installing (unattended) server=$SERVER enforce=$ENFORCE enterprise=${ENTERPRISE:-no}"
    else
        log "installing (sigma-agent-setup will prompt for settings)"
    fi

    if command -v dnf >/dev/null 2>&1; then
        dnf install -y SigmaAgent
    else
        yum install -y SigmaAgent
    fi
}

install_linux() {
    if command -v apt-get >/dev/null 2>&1 || command -v dpkg >/dev/null 2>&1; then
        install_deb
    elif command -v dnf >/dev/null 2>&1 || command -v yum >/dev/null 2>&1 || command -v rpm >/dev/null 2>&1; then
        install_rpm
    else
        err "no supported package manager found (need apt or dnf/yum)."
    fi
}

case "$OS" in
    Darwin) install_macos ;;
    Linux)  install_linux ;;
    *)      err "unsupported OS '$OS'" ;;
esac

log "done."
if [ -z "$SERVER" ]; then
    log "note: the agent will not start until a central server is configured."
fi
