#!/bin/bash
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
# Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
#
# Phase 5 (#495) — provision the toolbox-mitm LXC container.
# Idempotent : safe to re-run. Builds rootfs if missing, refreshes config
# if drifted, ensures container is started after install.
#
# Reused by debian/postinst.

set -euo pipefail

# Phase 7 follow-up (#498) — accept --target {transparent,wg} so we can
# provision the second LXC (toolbox-mitm-wg) the same way. Default keeps
# the historical transparent-mitm behaviour (#495).
TARGET="${1:-transparent}"
case "$TARGET" in
    --target=wg|wg)        TARGET="wg" ;;
    --target=transparent|transparent|"") TARGET="transparent" ;;
    --help|-h)
        cat <<EOF
Usage: $(basename "$0") [transparent|wg]

    transparent  (default) — toolbox-mitm LXC at 10.100.0.61
                              for the captive-AP mitm (#495).
    wg                     — toolbox-mitm-wg LXC at 10.100.0.62
                              for the R3 WG mitm (#498). Replaces
                              the host-side secubox-toolbox-mitm-wg
                              service so nothing wg-related runs on
                              the host except wg-quick.
EOF
        exit 0
        ;;
    *) echo "unknown target: $TARGET (use 'transparent' or 'wg')" >&2; exit 2 ;;
esac

if [ "$TARGET" = "wg" ]; then
    CONTAINER="toolbox-mitm-wg"
    CONFIG_TEMPLATE="/usr/share/secubox/toolbox/lxc/toolbox-mitm-wg.conf.template"
    SVC_TEMPLATE="/usr/share/secubox/toolbox/lxc/toolbox-mitm-wg.service.template"
    SVC_NAME="secubox-toolbox-mitm-wg.service"
    CONTAINER_IP="10.100.0.62"
else
    CONTAINER="toolbox-mitm"
    CONFIG_TEMPLATE="/usr/share/secubox/toolbox/lxc/toolbox-mitm.conf.template"
    SVC_TEMPLATE="/usr/share/secubox/toolbox/lxc/toolbox-mitm.service.template"
    SVC_NAME="secubox-toolbox-mitm.service"
    CONTAINER_IP="10.100.0.61"
fi

LXC_PATH="/var/lib/lxc"
ROOTFS="${LXC_PATH}/${CONTAINER}/rootfs"
CONFIG_TARGET="/etc/lxc/${CONTAINER}.conf"

log() {
    printf '[toolbox-lxc-provision] %s\n' "$*"
}

err() {
    printf '[toolbox-lxc-provision] ERROR: %s\n' "$*" >&2
    exit 1
}

# Prereq checks
command -v lxc-create >/dev/null || err "lxc-create not found (apt install lxc)"
command -v lxc-start >/dev/null || err "lxc-start not found"
[ -f "$CONFIG_TEMPLATE" ] || err "config template missing: $CONFIG_TEMPLATE"

# ── Build rootfs if absent ──
# Phase 7 (#498) — use the `download` template instead of `debian`.
# The `debian` template requires PRIVILEGED LXC ; the download template
# ships a pre-baked rootfs and works for unprivileged (which matches the
# existing mitmproxy LXC convention).
if [ ! -d "$ROOTFS/etc" ]; then
    log "rootfs missing, downloading Debian 12 bookworm arm64 image"
    lxc-create -n "$CONTAINER" -t download -- \
        --dist=debian --release=bookworm --arch=arm64 \
        || err "lxc-create failed"
    log "rootfs built — package install deferred until network is up"
else
    log "rootfs exists, skipping create"
fi

# ── Install/refresh config ──
# Phase 7 (#498) — LXC reads its config from /data/lxc/<name>/config (or
# /var/lib/lxc/<name>/config), NOT from /etc/lxc/<name>.conf. Write a
# clean, deduped config that combines :
#   - the auto-generated idmap (chosen at create time, encodes
#     unprivileged uid range)
#   - the actual rootfs path (also auto-detected)
#   - everything else from our template
log "writing clean LXC live config"
LIVE_CONFIG=""
LIVE_ROOTFS=""
for cand in "/data/lxc/${CONTAINER}" "/var/lib/lxc/${CONTAINER}"; do
    if [ -f "$cand/config" ]; then
        LIVE_CONFIG="$cand/config"
        LIVE_ROOTFS="$cand/rootfs"
        break
    fi
done
[ -n "$LIVE_CONFIG" ] || err "live LXC config not found for $CONTAINER"

# Stop the container so the new config + mounts take effect cleanly.
lxc-info -n "$CONTAINER" 2>/dev/null | grep -q 'State.*RUNNING' \
    && lxc-stop -n "$CONTAINER" -t 5 -k 2>/dev/null && sleep 1 || true

# Phase 7 (#498) — privilege mode :
#   transparent (Phase 5)  → unprivileged (lxc.idmap preserved from
#                            auto-gen) — matches the captive-mitm
#                            container convention.
#   wg                     → PRIVILEGED — required for the bind mount
#                            of /etc/secubox/toolbox/ca-wg (0750
#                            root:secubox-toolbox) which is invisible
#                            to a non-privileged LXC's mapped UID.
#                            Matches the existing mitmproxy WAF LXC.
if [ "$TARGET" = "wg" ]; then
    IDMAP_LINES=""
    INCLUDE_USERNS=""
    log "wg target → PRIVILEGED LXC (no idmap, matches mitmproxy WAF convention)"
else
    IDMAP_LINES=$(grep -E '^lxc\.idmap' "$LIVE_CONFIG" || true)
    INCLUDE_USERNS="lxc.include = /usr/share/lxc/config/userns.conf"
fi

# Compose final config :
TMP_CONFIG=$(mktemp)
{
    echo "# Generated by secubox-toolbox-lxc-provision (target=$TARGET)"
    echo "# DO NOT EDIT — re-run the provisioner to refresh."
    echo
    echo "lxc.include = /usr/share/lxc/config/common.conf"
    [ -n "$INCLUDE_USERNS" ] && echo "$INCLUDE_USERNS"
    echo
    [ -n "$IDMAP_LINES" ] && { echo "$IDMAP_LINES"; echo; }
    echo "lxc.rootfs.path = dir:${LIVE_ROOTFS}"
    # Then everything from the template EXCEPT lxc.rootfs.path (we just
    # set the correct one above) and any include directives that the
    # auto-gen already covers.
    grep -vE '^lxc\.rootfs\.path|^lxc\.include' "$CONFIG_TEMPLATE"
} > "$TMP_CONFIG"

cp -f "$TMP_CONFIG" "$LIVE_CONFIG"
chmod 0640 "$LIVE_CONFIG"
rm -f "$TMP_CONFIG"
# Also write the canonical path so future invocations can grep it.
install -d -m 0755 /etc/lxc
cp -f "$LIVE_CONFIG" "$CONFIG_TARGET"
chmod 0644 "$CONFIG_TARGET"

# Bind-mount paths must exist on the host (Phase 7 #498 — wg variant adds
# /etc/secubox/toolbox/ca-wg + /etc/secubox/toolbox/wg + a dedicated log dir).
if [ "$TARGET" = "wg" ]; then
    PATHS_TO_ENSURE=(
        /etc/secubox/toolbox/ca-wg
        /etc/secubox/toolbox/wg
        /var/lib/secubox/toolbox
        /var/log/secubox/toolbox-mitm-wg
        /usr/lib/secubox/toolbox/mitmproxy_addons
    )
else
    PATHS_TO_ENSURE=(
        /etc/secubox/toolbox/ca
        /var/lib/secubox/toolbox
        /var/log/secubox/toolbox-mitm
        /usr/lib/secubox/toolbox/mitmproxy_addons
    )
fi
for p in "${PATHS_TO_ENSURE[@]}"; do
    mkdir -p "$p"
done

# Salt file required — postinst of toolbox creates it before lxc-provision
if [ ! -f /etc/secubox/secrets/toolbox-mac-salt ]; then
    log "WARN: /etc/secubox/secrets/toolbox-mac-salt missing — will create at toolbox postinst"
fi

# ── Install mitm service inside the container ──
# Phase 7 (#498) — unprivileged LXCs have uid-shifted rootfs ; writing
# via lxc-attach is the only reliable way (host-side cp from /var/lib/lxc
# fails because the root path is at /data/lxc and owned by mapped uid).
if [ -f "$SVC_TEMPLATE" ]; then
    log "deploying $SVC_NAME into container via lxc-attach"
    # We need the container running for lxc-attach to work
    lxc-info -n "$CONTAINER" 2>/dev/null | grep -q 'State.*RUNNING' \
        || { lxc-start -n "$CONTAINER" && sleep 4; }
    cat "$SVC_TEMPLATE" | lxc-attach -n "$CONTAINER" -- bash -c "
        mkdir -p /etc/systemd/system
        cat > /etc/systemd/system/$SVC_NAME
        chmod 0644 /etc/systemd/system/$SVC_NAME
        systemctl daemon-reload
        systemctl enable $SVC_NAME 2>&1 | tail -1
    "
fi

# Phase 7 (#498) — WG variant : the LXC needs the host's bypass + peers
# state and the addons exactly as the host service used to load them. The
# bind-mounts in the template handle that, but we also need to make sure
# the addons + state files exist on the host BEFORE the container boots
# (LXC fails to start if a bind-mount source is missing without
# `optional`). The seed below is a no-op if the files already exist.
if [ "$TARGET" = "wg" ]; then
    install -d -m 0750 -o secubox-toolbox -g secubox-toolbox /var/lib/secubox/toolbox 2>/dev/null || true
    touch /var/lib/secubox/toolbox/mitm-bypass.conf 2>/dev/null || true
    touch /var/lib/secubox/toolbox/mitm-bypass-dynamic.conf 2>/dev/null || true
fi

# ── Start the container (or restart if running) ──
if lxc-info -n "$CONTAINER" 2>/dev/null | grep -q 'State.*RUNNING'; then
    log "container running, restarting to pick up config"
    lxc-stop -n "$CONTAINER" -t 5 -k 2>/dev/null || true
    sleep 2
fi

log "starting container $CONTAINER"
lxc-start -n "$CONTAINER" || err "lxc-start failed"

# Wait for network
for _ in 1 2 3 4 5; do
    if lxc-info -n "$CONTAINER" 2>/dev/null | grep -q "$CONTAINER_IP"; then
        log "container network up : $CONTAINER_IP"
        break
    fi
    sleep 1
done

# Phase 7 (#498) — UNPRIVILEGED LXC doesn't auto-apply
# `lxc.net.0.ipv4.address` (kernel refuses to set the addr without
# CAP_NET_ADMIN on the container). Push the static IP + default route
# via lxc-attach + write a systemd-networkd file so it survives the
# next boot too.
sleep 2
lxc-attach -n "$CONTAINER" -- bash -c "
    # Runtime : assign now so the rest of provision can apt-install etc.
    ip addr show eth0 | grep -q '${CONTAINER_IP}/' || ip addr add ${CONTAINER_IP}/24 dev eth0 2>/dev/null
    ip route | grep -q '^default' || ip route add default via 10.100.0.1 2>/dev/null
    # Boot-survival : minimal systemd-networkd profile.
    mkdir -p /etc/systemd/network
    cat > /etc/systemd/network/10-eth0.network <<NEOF
[Match]
Name=eth0

[Network]
Address=${CONTAINER_IP}/24
Gateway=10.100.0.1
DNS=10.100.0.1
DNS=1.1.1.1
NEOF
    systemctl enable systemd-networkd 2>/dev/null || true
    # DNS via resolv.conf so apt works straight away.
    cat > /etc/resolv.conf <<REOF
nameserver 10.100.0.1
nameserver 1.1.1.1
REOF
" 2>&1 || log "WARN runtime IP assignment failed"

# Install runtime packages now that the network is up (mitmproxy itself
# is bind-mounted from /opt/mitmproxy-toolbox so we only need python3 +
# basics). Idempotent : apt install of already-installed packages is a
# fast no-op.
log "installing runtime packages inside container (python3 + curl + procps)"
lxc-attach -n "$CONTAINER" -- bash -c "
    apt-get update -qq 2>&1 | tail -2
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends python3 curl procps 2>&1 | tail -3
" || log "WARN apt install failed — container may need manual fix"

# Phase 7 (#498) — WG cutover hints. The host's secubox-toolbox-mitm-wg
# service can be replaced once this LXC is verified working. We don't
# touch the host service automatically — operator decides the cutover
# moment with `secubox-toolbox-mitm-wg-cutover` (separate tool).
if [ "$TARGET" = "wg" ]; then
    cat <<EOF

Next steps to cut over to the LXC mitm-wg (manual — service moment) :

  1. Verify the LXC accepts traffic on port 8081 :
       lxc-attach -n toolbox-mitm-wg -- ss -tln | grep 8081

  2. Repoint the nft DNAT from 10.99.1.1:8081 to 10.100.0.62:8081 :
       nft replace rule inet wg-toolbox prerouting \\
           handle <h> iif wg-toolbox tcp dport 443 dnat ip to 10.100.0.62:8081

  3. Stop + disable the host service :
       systemctl disable --now secubox-toolbox-mitm-wg.service

  4. Update /etc/nftables.d/secubox-toolbox-wg.nft to point at .62
     (so next reboot loads the new target).
EOF
fi

log "provision complete (target=$TARGET, ip=$CONTAINER_IP)"
