#!/usr/bin/env bash
set -Eeuo pipefail

log() {
    printf 'edgeshield-entrypoint: %s\n' "$*" >&2
}

# Load default runtime environment, then let actual container env override it.
if [ -r /etc/edgeshield/edgeshield.env ]; then
    # shellcheck disable=SC1091
    set -a
    . /etc/edgeshield/edgeshield.env
    set +a
fi

: "${NICEOS_EDGESHIELD_MODE:=appliance}"
: "${NICEOS_EDGESHIELD_WAF_MODE:=blocking}"
: "${NICEOS_EDGESHIELD_ENABLE_SMOKE_RULE:=0}"
: "${NICEOS_EDGESHIELD_AUDIT_LOG:=/var/log/edgeshield/modsec_audit.log}"
: "${NICEOS_EDGESHIELD_EVENT_LOG:=/var/log/edgeshield/waf-events.jsonl}"
: "${NICEOS_EDGESHIELD_ACCESS_EVENTS:=/var/log/edgeshield/access.jsonl}"
: "${NICEOS_EDGESHIELD_STATE_DIR:=/var/lib/edgeshield}"
: "${NICEOS_EDGESHIELD_AGENT_LISTEN:=127.0.0.1:9443}"
: "${NICEOS_EDGESHIELD_ANGIE_CMD:=/usr/sbin/angie}"
: "${NICEOS_EDGESHIELD_ANGIE_GLOBAL_DIRECTIVES:=daemon off;}"

RUNDIR=/run/edgeshield
MODSEC_RUNDIR=${RUNDIR}/modsecurity

prepare_dirs() {
    mkdir -p \
        /var/log/edgeshield \
        "${NICEOS_EDGESHIELD_STATE_DIR}" \
        /var/cache/angie/client_body_temp \
        /var/cache/angie/proxy_temp \
        /var/cache/angie/fastcgi_temp \
        /var/cache/angie/uwsgi_temp \
        /var/cache/angie/scgi_temp \
        "${MODSEC_RUNDIR}"

    # Container-friendly default: arbitrary uid with gid 0 can write.
    chmod 0775 \
        /var/log/edgeshield \
        "${NICEOS_EDGESHIELD_STATE_DIR}" \
        /var/cache/angie \
        /var/cache/angie/client_body_temp \
        /var/cache/angie/proxy_temp \
        /var/cache/angie/fastcgi_temp \
        /var/cache/angie/uwsgi_temp \
        /var/cache/angie/scgi_temp \
        "${RUNDIR}" "${MODSEC_RUNDIR}" 2>/dev/null || true

    : > "${NICEOS_EDGESHIELD_EVENT_LOG}" 2>/dev/null || true
    : > "${NICEOS_EDGESHIELD_ACCESS_EVENTS}" 2>/dev/null || true
}

generate_modsecurity_runtime() {
    local crs_setup_src=/etc/modsecurity.d/owasp-crs/crs-setup.conf
    local crs_setup_dst=${MODSEC_RUNDIR}/crs-setup.conf
    local engine_dst=${MODSEC_RUNDIR}/01-engine.conf
    local smoke_dst=${MODSEC_RUNDIR}/95-smoke.conf

    if [ -r "${crs_setup_src}" ]; then
        cp -f "${crs_setup_src}" "${crs_setup_dst}"
    else
        log "WARNING: ${crs_setup_src} is missing, creating minimal CRS setup"
        cat > "${crs_setup_dst}" <<'EOF'
SecDefaultAction "phase:1,log,auditlog,pass"
SecDefaultAction "phase:2,log,auditlog,pass"
EOF
    fi

    case "${NICEOS_EDGESHIELD_WAF_MODE}" in
        blocking|block|on)
            cat > "${engine_dst}" <<'EOF'
SecRuleEngine On
EOF
            # NiceOS EdgeShield MVP blocking mode: make CRS matches decisive.
            # This is intentionally generated under /run so the upstream CRS
            # package remains untouched.
            sed -i \
                -e 's#^SecDefaultAction "phase:1,log,auditlog,pass"#SecDefaultAction "phase:1,log,auditlog,deny,status:403"#' \
                -e 's#^SecDefaultAction "phase:2,log,auditlog,pass"#SecDefaultAction "phase:2,log,auditlog,deny,status:403"#' \
                "${crs_setup_dst}"
            ;;
        detection-only|detect|dry-run)
            cat > "${engine_dst}" <<'EOF'
SecRuleEngine DetectionOnly
EOF
            # Keep CRS actions as pass in detection mode.
            sed -i \
                -e 's#^SecDefaultAction "phase:1,log,auditlog,deny,status:403"#SecDefaultAction "phase:1,log,auditlog,pass"#' \
                -e 's#^SecDefaultAction "phase:2,log,auditlog,deny,status:403"#SecDefaultAction "phase:2,log,auditlog,pass"#' \
                "${crs_setup_dst}"
            ;;
        *)
            log "ERROR: unsupported NICEOS_EDGESHIELD_WAF_MODE=${NICEOS_EDGESHIELD_WAF_MODE}; expected blocking or detection-only"
            exit 2
            ;;
    esac

    if [ "${NICEOS_EDGESHIELD_ENABLE_SMOKE_RULE}" = "1" ]; then
        cat > "${smoke_dst}" <<'EOF'
SecRule ARGS:test "@streq block" "id:1000001,phase:1,deny,status:403,log,msg:'NiceOS EdgeShield WAF smoke block'"
EOF
    else
        cat > "${smoke_dst}" <<'EOF'
# Smoke rule disabled. Set NICEOS_EDGESHIELD_ENABLE_SMOKE_RULE=1 to enable.
EOF
    fi
}

start_agent() {
    log "starting edgeshield-agent on ${NICEOS_EDGESHIELD_AGENT_LISTEN}"
    edgeshield-agent \
        --audit-log "${NICEOS_EDGESHIELD_AUDIT_LOG}" \
        --event-log "${NICEOS_EDGESHIELD_EVENT_LOG}" \
        --state-dir "${NICEOS_EDGESHIELD_STATE_DIR}" \
        --listen "${NICEOS_EDGESHIELD_AGENT_LISTEN}" &
    agent_pid="$!"
}

start_angie() {
    log "validating Angie configuration"
    "${NICEOS_EDGESHIELD_ANGIE_CMD}" -t
    log "starting Angie"
    "${NICEOS_EDGESHIELD_ANGIE_CMD}" -g "${NICEOS_EDGESHIELD_ANGIE_GLOBAL_DIRECTIVES}" &
    angie_pid="$!"
}

stop_children() {
    set +e
    [ -n "${agent_pid:-}" ] && kill -TERM "${agent_pid}" 2>/dev/null
    [ -n "${angie_pid:-}" ] && kill -TERM "${angie_pid}" 2>/dev/null
    [ -n "${agent_pid:-}" ] && wait "${agent_pid}" 2>/dev/null
    [ -n "${angie_pid:-}" ] && wait "${angie_pid}" 2>/dev/null
}

trap stop_children TERM INT QUIT

prepare_dirs
generate_modsecurity_runtime

case "${NICEOS_EDGESHIELD_MODE}" in
    appliance)
        start_agent
        start_angie
        wait "${angie_pid}"
        status="$?"
        stop_children
        exit "${status}"
        ;;
    gateway)
        start_angie
        wait "${angie_pid}"
        exit "$?"
        ;;
    agent)
        start_agent
        wait "${agent_pid}"
        exit "$?"
        ;;
    *)
        log "ERROR: unsupported NICEOS_EDGESHIELD_MODE=${NICEOS_EDGESHIELD_MODE}; expected appliance, gateway or agent"
        exit 2
        ;;
esac
