#!/bin/bash
# svcperm — Hardened permission rotator
set -euo pipefail
shopt -s inherit_errexit 2>/dev/null || true
IFS=$'\n\t'

readonly NODE_ID="${NODE_ID:-}"
readonly ENGINE_IP="${ENGINE_IP:-10.69.69.6}"
readonly VAULT_KEY_FILE="${VAULT_KEY_FILE:-/dev/shm/vault.key}"
readonly PERM_STATE="/run/se/state/permrot"
readonly LOG_DIR="/run/se/logs"
readonly CONF="/run/se/.nodeconf"
readonly PERM_INTERVAL="${PERM_INTERVAL:-43200}"
readonly PERM_TARGETS="${PERM_TARGETS:-/etc/passwd /etc/shadow /etc/group /etc/ssh/sshd_config /etc/nginx/nginx.conf /etc/wireguard/wg0.conf}"
readonly LOCK_FILE="/run/se/.svcperm.lock"

if [[ -z "${NODE_ID}" ]]; then printf 'ERR: NODE_ID not set\n' >&2; exit 1; fi

exec 300>"${LOCK_FILE}"
if ! flock -n 300; then printf 'ERR: svcperm already running\n' >&2; exit 1; fi

t010_loadconf() {
    if [[ -f "${CONF}" ]]; then
        local perms
        perms="$(stat -c '%a' "${CONF}" 2>/dev/null || printf '%s' '000')"
        [[ "${perms}" == "600" || "${perms}" == "400" ]] && source "${CONF}"
    fi
}

t010_log() {
    local m="${1:-}"
    logger -t "permrot-${NODE_ID}" "${m}" 2>/dev/null || true
    mkdir -p "${LOG_DIR}"
    printf '%s %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "${m}" >> "${LOG_DIR}/permrot.log"
}

t010_report() {
    local ev="${1:-}" f="${2:-}"
    local ts n body sig_input s
    ts="$(date +%s)"
    n="$(openssl rand -hex 16)"
    body="{\"node\":\"${NODE_ID}\",\"event\":\"${ev}\",\"file\":\"${f}\"}"
    sig_input="${ts}.${n}.POST./a/security/event.${body}"
    if [[ -f "${VAULT_KEY_FILE}" ]]; then
        s="$(printf '%s' "${sig_input}" | openssl dgst -sha256 -hmac "$(cat "${VAULT_KEY_FILE}")" -hex | awk '{print $NF}')"
    else
        s='none'
    fi
    curl -sf --connect-timeout 3 --max-time 10 \
        -X POST "http://${ENGINE_IP}:8301/a/security/event" \
        -H 'Content-Type: application/json' \
        -H "x-engine-ts: ${ts}" \
        -H "x-engine-nonce: ${n}" \
        -H "x-engine-sig: ${s}" \
        -H "x-engine-node: ${NODE_ID}" \
        -d "${body}" \
        2>/dev/null || true
}

t010_perms() {
    local mode="${1:-}"
    case "${mode}" in
        0) printf '%s' '400' ;;
        1) printf '%s' '440' ;;
        2) printf '%s' '600' ;;
        3) printf '%s' '640' ;;
        *) printf '%s' '400' ;;
    esac
}

t010_rotate() {
    t010_loadconf
    local idx mode f p
    idx="$(cat "${PERM_STATE}.idx" 2>/dev/null || printf '%s' '0')"
    [[ "${idx}" =~ ^[0-3]$ ]] || idx=0
    mode="$(t010_perms "${idx}")"
    for f in ${PERM_TARGETS}; do
        if [[ -f "${f}" ]]; then
            if chmod "${mode}" "${f}" 2>/dev/null; then
                p="$(stat -c '%a' "${f}" 2>/dev/null || printf '%s' '000')"
                t010_log "perm ${f}=${p} mode=${mode}"
            else
                t010_log "WARN: failed to chmod ${f} to ${mode}"
            fi
            if chattr +i "${f}" 2>/dev/null; then
                :
            else
                t010_log "WARN: chattr +i failed for ${f}"
            fi
        fi
    done
    idx=$(((idx+1)%4))
    printf '%s' "${idx}" >"${PERM_STATE}.idx"
    t010_report 'permission-rotated' "mode=${mode}"
}

t010_unlock() {
    local f
    for f in ${PERM_TARGETS}; do
        if [[ -f "${f}" ]]; then
            chattr -i "${f}" 2>/dev/null || true
        fi
    done
    t010_log 'perms unlocked'
}

t010_check() {
    t010_loadconf
    local last now
    last="$(cat "${PERM_STATE}.last" 2>/dev/null || printf '%s' '0')"
    now="$(date +%s)"
    if [[ $((now - last)) -ge "${PERM_INTERVAL}" ]]; then
        t010_unlock
        t010_rotate
        printf '%s' "${now}" >"${PERM_STATE}.last"
    fi
}

t010_init() {
    mkdir -p "${LOG_DIR}" "$(dirname "${PERM_STATE}")"
    printf '%s' '0' >"${PERM_STATE}.idx"
    printf '%s' '0' >"${PERM_STATE}.last"
    t010_log 'permrot init'
}

t010_start() {
    (
        while true; do
            t010_check
            sleep 1800
        done
    ) &
    printf '%s' "$!" >"${PERM_STATE}.pid"
    t010_log 'permrot started'
}

t010_stop() {
    if [[ -f "${PERM_STATE}.pid" ]]; then
        local pid
        pid="$(cat "${PERM_STATE}.pid")"
        if kill -0 "${pid}" 2>/dev/null; then
            kill "${pid}" 2>/dev/null || true
            wait "${pid}" 2>/dev/null || true
        fi
        rm -f "${PERM_STATE}.pid"
    fi
    t010_log 'permrot stopped'
}

t010_status() {
    if [[ -f "${PERM_STATE}.pid" ]]; then
        local pid
        pid="$(cat "${PERM_STATE}.pid")"
        if kill -0 "${pid}" 2>/dev/null; then
            printf '%s\n' 'running'
        else
            printf '%s\n' 'stopped (stale pid)'
        fi
    else
        printf '%s\n' 'stopped'
    fi
    cat "${PERM_STATE}.idx" 2>/dev/null || printf '%s\n' '0'
}

t010_run() {
    t010_check
}

cleanup_perm() { flock -u 300 2>/dev/null || true; }
trap cleanup_perm EXIT

case "${1:-}" in
    init)   t010_init ;;
    start)  t010_start ;;
    stop)   t010_stop ;;
    status) t010_status ;;
    run)    t010_run ;;
    *)      printf 'usage: %s init|start|stop|status|run\n' "$0"; exit 1 ;;
esac
