diff --git a/setup-npm-lxc.sh b/setup-npm-lxc.sh index 55e9d43..ac5792e 100644 --- a/setup-npm-lxc.sh +++ b/setup-npm-lxc.sh @@ -57,7 +57,7 @@ else echo "[1/9] Debian 12 template already available" fi -# --- Create the container --- +# --- Create the container (don't start yet - need AppArmor fix first) --- echo "[2/9] Creating LXC container ${CT_ID}..." pct create "${CT_ID}" "${CT_TEMPLATE}" \ --hostname "${CT_NAME}" \ @@ -68,16 +68,23 @@ pct create "${CT_ID}" "${CT_TEMPLATE}" \ --net0 "name=eth0,bridge=${BRIDGE},ip=${CT_IP},gw=${CT_GW},firewall=0" \ --nameserver "10.0.0.224" \ --onboot 1 \ - --start 1 \ --unprivileged 1 \ --features "nesting=1,keyctl=1" \ --startup "order=2,up=10" -echo "[3/9] Waiting for container to start..." +# --- Apply AppArmor fix for Docker-in-LXC --- +echo "[3/9] Applying AppArmor fix for Docker compatibility..." +CT_CONF="/etc/pve/lxc/${CT_ID}.conf" +if ! grep -q "lxc.apparmor.profile" "${CT_CONF}" 2>/dev/null; then + echo "lxc.apparmor.profile: unconfined" >> "${CT_CONF}" +fi +pct start "${CT_ID}" + +echo "[4/9] Waiting for container to start..." sleep 5 # --- Install Docker inside the container --- -echo "[4/9] Installing Docker..." +echo "[5/9] Installing Docker..." pct exec "${CT_ID}" -- bash -c ' apt-get update -qq apt-get install -y -qq ca-certificates curl gnupg @@ -92,7 +99,7 @@ pct exec "${CT_ID}" -- bash -c ' ' # --- Create Docker Compose config --- -echo "[5/9] Creating NPM Docker Compose configuration..." +echo "[6/9] Creating NPM Docker Compose configuration..." pct exec "${CT_ID}" -- bash -c " mkdir -p /opt/npm/data /opt/npm/letsencrypt cat > /opt/npm/docker-compose.yml << 'DCEOF' @@ -119,113 +126,102 @@ DCEOF " # --- Attempt data migration from HA addon --- -echo "[6/9] Attempting data migration from HA addon..." +echo "[7/9] Attempting data migration from HA addon..." MIGRATED=false -if ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no root@${HAOS_IP} "test -d /addon_data/${ADDON_SLUG}" 2>/dev/null; then - echo " Found NPM addon data. Copying..." - TMPDIR=$(mktemp -d) +# Try SSH first (ports 22222 and 22) +for PORT in 22222 22; do + if ssh -o ConnectTimeout=3 -o StrictHostKeyChecking=no -p ${PORT} \ + root@${HAOS_IP} "test -d /addon_data/${ADDON_SLUG}" 2>/dev/null; then + echo " SSH connected (port ${PORT}), copying data..." + TMPDIR=$(mktemp -d) + scp -P ${PORT} -r -o StrictHostKeyChecking=no \ + root@${HAOS_IP}:/addon_data/${ADDON_SLUG}/ "${TMPDIR}/" 2>/dev/null + ADDON_DATA="${TMPDIR}/${ADDON_SLUG}" - # Copy entire addon data directory from HAOS - scp -r -o StrictHostKeyChecking=no root@${HAOS_IP}:/addon_data/${ADDON_SLUG}/ "${TMPDIR}/" 2>/dev/null - ADDON_DATA="${TMPDIR}/${ADDON_SLUG}" + if [ -d "${ADDON_DATA}" ]; then + DB_SRC=$(find "${ADDON_DATA}" -name "database.sqlite" -type f 2>/dev/null | head -1) + [ -n "${DB_SRC}" ] && pct push "${CT_ID}" "${DB_SRC}" /opt/npm/data/database.sqlite && echo " DB migrated" - if [ -d "${ADDON_DATA}" ]; then - echo " Addon data downloaded to ${TMPDIR}" - - # --- Migrate SQLite database --- - # HA addon NPM may store the DB in various locations - DB_SRC="" - for candidate in \ - "${ADDON_DATA}/database.sqlite" \ - "${ADDON_DATA}/data/database.sqlite" \ - "${ADDON_DATA}/nginx/database.sqlite"; do - if [ -f "$candidate" ]; then - DB_SRC="$candidate" - break + CERT_DIR=$(find "${ADDON_DATA}" -type d -name "letsencrypt" 2>/dev/null | head -1) + if [ -n "${CERT_DIR}" ]; then + tar -czf "${TMPDIR}/c.tar.gz" -C "${CERT_DIR}" . 2>/dev/null + pct push "${CT_ID}" "${TMPDIR}/c.tar.gz" /tmp/c.tar.gz + pct exec "${CT_ID}" -- bash -c 'tar xzf /tmp/c.tar.gz -C /opt/npm/letsencrypt/ && rm /tmp/c.tar.gz' + echo " Certs migrated" fi - done - - if [ -n "${DB_SRC}" ]; then - echo " Found database: ${DB_SRC}" - pct push "${CT_ID}" "${DB_SRC}" /opt/npm/data/database.sqlite - echo " Database migrated (proxy hosts, users, settings)" - else - echo " WARNING: database.sqlite not found in addon data" - # List what we did find for debugging - echo " Contents of addon data:" - find "${ADDON_DATA}" -maxdepth 3 -type f | head -30 + MIGRATED=true fi - - # --- Migrate Let's Encrypt certificates --- - CERTS_SRC="" - for candidate in \ - "${ADDON_DATA}/letsencrypt" \ - "${ADDON_DATA}/ssl" \ - "${ADDON_DATA}/data/letsencrypt"; do - if [ -d "$candidate" ]; then - CERTS_SRC="$candidate" - break - fi - done - - if [ -n "${CERTS_SRC}" ]; then - echo " Found certificates: ${CERTS_SRC}" - # Tar up certs, push to LXC, extract - tar -czf "${TMPDIR}/certs.tar.gz" -C "${CERTS_SRC}" . 2>/dev/null - pct push "${CT_ID}" "${TMPDIR}/certs.tar.gz" /tmp/certs.tar.gz - pct exec "${CT_ID}" -- bash -c ' - mkdir -p /opt/npm/letsencrypt - tar -xzf /tmp/certs.tar.gz -C /opt/npm/letsencrypt/ - rm /tmp/certs.tar.gz - ' - CERT_COUNT=$(find "${CERTS_SRC}" -name "*.pem" -o -name "*.crt" 2>/dev/null | wc -l) - echo " Certificates migrated (${CERT_COUNT} cert files)" - else - echo " No certificate directory found (will need re-issue)" - fi - - # --- Migrate custom Nginx configs --- - for custom_dir in \ - "${ADDON_DATA}/nginx/custom" \ - "${ADDON_DATA}/data/nginx/custom"; do - if [ -d "$custom_dir" ] && [ "$(ls -A "$custom_dir" 2>/dev/null)" ]; then - echo " Found custom Nginx configs: ${custom_dir}" - tar -czf "${TMPDIR}/custom.tar.gz" -C "${custom_dir}" . 2>/dev/null - pct push "${CT_ID}" "${TMPDIR}/custom.tar.gz" /tmp/custom.tar.gz - pct exec "${CT_ID}" -- bash -c ' - mkdir -p /opt/npm/data/nginx/custom - tar -xzf /tmp/custom.tar.gz -C /opt/npm/data/nginx/custom/ - rm /tmp/custom.tar.gz - ' - echo " Custom configs migrated" - break - fi - done - - MIGRATED=true + rm -rf "${TMPDIR}" + break fi +done - rm -rf "${TMPDIR}" -else - echo " Could not SSH to HAOS at ${HAOS_IP}." - echo " Make sure Terminal & SSH addon is installed." +# Try HA Supervisor partial backup if SSH failed +if [ "$MIGRATED" = false ]; then + echo " SSH failed. Trying HA Supervisor partial backup..." + echo " (requires HA long-lived token set in HA_TOKEN env var)" + + if [ -n "${HA_TOKEN:-}" ]; then + HA_URL="http://${HAOS_IP}:8123" + TMPDIR=$(mktemp -d) + BACKUP_SLUG=$(curl -s -X POST \ + -H "Authorization: Bearer ${HA_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "{\"name\":\"npm-migration\",\"addons\":[\"${ADDON_SLUG}\"],\"folders\":[]}" \ + "${HA_URL}/api/hassio/backups/new/partial" 2>/dev/null \ + | python3 -c "import sys,json; print(json.load(sys.stdin).get('data',{}).get('slug',''))" 2>/dev/null) + + if [ -n "${BACKUP_SLUG}" ]; then + echo " Backup created (${BACKUP_SLUG}), downloading..." + curl -s -o "${TMPDIR}/backup.tar" \ + -H "Authorization: Bearer ${HA_TOKEN}" \ + "${HA_URL}/api/hassio/backups/${BACKUP_SLUG}/download" 2>/dev/null + + if [ -s "${TMPDIR}/backup.tar" ]; then + cd "${TMPDIR}" && tar xf backup.tar 2>/dev/null || true + ADDON_TAR=$(find "${TMPDIR}" -name "*nginxproxymanager*" -name "*.tar.gz" 2>/dev/null | head -1) + if [ -n "${ADDON_TAR}" ]; then + mkdir -p "${TMPDIR}/npm_data" + tar xzf "${ADDON_TAR}" -C "${TMPDIR}/npm_data/" 2>/dev/null || true + DB_SRC=$(find "${TMPDIR}/npm_data" -name "database.sqlite" -type f 2>/dev/null | head -1) + [ -n "${DB_SRC}" ] && pct push "${CT_ID}" "${DB_SRC}" /opt/npm/data/database.sqlite && echo " DB migrated" + CERT_DIR=$(find "${TMPDIR}/npm_data" -type d -name "letsencrypt" 2>/dev/null | head -1) + if [ -n "${CERT_DIR}" ]; then + tar -czf "${TMPDIR}/c.tar.gz" -C "${CERT_DIR}" . 2>/dev/null + pct push "${CT_ID}" "${TMPDIR}/c.tar.gz" /tmp/c.tar.gz + pct exec "${CT_ID}" -- bash -c 'tar xzf /tmp/c.tar.gz -C /opt/npm/letsencrypt/ && rm /tmp/c.tar.gz' + echo " Certs migrated" + fi + MIGRATED=true + fi + fi + curl -s -X DELETE -H "Authorization: Bearer ${HA_TOKEN}" \ + "${HA_URL}/api/hassio/backups/${BACKUP_SLUG}" 2>/dev/null || true + fi + rm -rf "${TMPDIR}" + fi +fi + +if [ "$MIGRATED" = false ]; then echo "" - echo " MANUAL MIGRATION:" - echo " From HAOS SSH terminal, run:" - echo " cd /addon_data/${ADDON_SLUG}" - echo " tar -czf /tmp/npm-backup.tar.gz ." - echo " scp /tmp/npm-backup.tar.gz root@10.0.0.240:/tmp/" - echo "" - echo " Then on Proxmox (10.0.0.240):" - echo " pct push ${CT_ID} /tmp/npm-backup.tar.gz /tmp/npm-backup.tar.gz" - echo " pct exec ${CT_ID} -- bash -c '" - echo " tar -xzf /tmp/npm-backup.tar.gz -C /opt/npm/data/" - echo " rm /tmp/npm-backup.tar.gz'" + echo " AUTO-MIGRATION FAILED." + echo " Manual steps after setup:" + echo " 1. HA > Settings > Backups > Create > Partial" + echo " Select ONLY 'Nginx Proxy Manager', create it" + echo " 2. Download the backup .tar file" + echo " 3. Copy to Proxmox: scp backup.tar root@10.0.0.240:/tmp/" + echo " 4. Extract and push the database:" + echo " cd /tmp && tar xf *.tar" + echo " tar xzf *nginxproxymanager*.tar.gz -C /tmp/npm_extract/" + echo " find /tmp/npm_extract -name database.sqlite" + echo " pct exec ${CT_ID} -- bash -c 'cd /opt/npm && docker compose down'" + echo " pct push ${CT_ID} /database.sqlite /opt/npm/data/database.sqlite" + echo " pct exec ${CT_ID} -- bash -c 'cd /opt/npm && docker compose up -d'" fi # --- Start NPM --- -echo "[7/9] Starting Nginx Proxy Manager..." +echo "[8/9] Starting Nginx Proxy Manager..." pct exec "${CT_ID}" -- bash -c ' cd /opt/npm docker compose up -d @@ -235,7 +231,7 @@ pct exec "${CT_ID}" -- bash -c ' ' # --- Create systemd service --- -echo "[8/9] Creating systemd service for auto-start..." +echo "[9/9] Creating systemd service for auto-start..." pct exec "${CT_ID}" -- bash -c ' cat > /etc/systemd/system/npm.service << "SVCEOF" [Unit] @@ -259,17 +255,7 @@ SVCEOF systemctl enable npm.service ' -# --- Verify --- -echo "[9/9] Verifying NPM is accessible..." CT_IP_CLEAN=$(echo "${CT_IP}" | cut -d'/' -f1) -sleep 5 -HTTP_CODE=$(pct exec "${CT_ID}" -- curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:81/ 2>/dev/null || echo "000") -if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "302" ]; then - echo " NPM admin panel responding (HTTP ${HTTP_CODE})" -else - echo " NPM may still be starting up (HTTP ${HTTP_CODE})" - echo " Give it another minute, then check http://${CT_IP_CLEAN}:81" -fi echo "" echo "============================================" @@ -281,15 +267,8 @@ echo " HTTP: ${CT_IP_CLEAN}:80" echo " HTTPS: ${CT_IP_CLEAN}:443" echo "" if [ "$MIGRATED" = true ]; then - echo " Data was migrated from HA addon!" - echo " Your proxy hosts and SSL certs should be intact." - echo "" - echo " Default login (if DB migration succeeded):" - echo " Use your existing NPM credentials." - echo "" - echo " If login fails (schema mismatch), use defaults:" - echo " Email: admin@example.com" - echo " Password: changeme" + echo " Data migrated from HA addon." + echo " Login with your existing NPM credentials." else echo " Default login (fresh install):" echo " Email: admin@example.com" @@ -297,28 +276,10 @@ else echo " >>> CHANGE THIS IMMEDIATELY <<<" fi echo "" -echo " CRITICAL NEXT STEPS:" -echo "" -echo " 1. Verify all proxy hosts work at http://${CT_IP_CLEAN}:81" -echo "" -echo " 2. Update OPNsense port forwards:" -echo " Firewall > NAT > Port Forward" -echo " Change destination for ports 80/443:" -echo " OLD: 10.0.0.55 (HAOS)" -echo " NEW: ${CT_IP_CLEAN} (NPM LXC)" -echo "" -echo " 3. Update any DNS records pointing to the old IP" -echo " (all *.hideawaygaming.com.au should resolve" -echo " to your WAN IP, which OPNsense forwards)" -echo "" -echo " 4. If certs didn't migrate, re-request them:" -echo " NPM Admin > SSL Certificates > Add" -echo " Use DNS challenge or HTTP challenge" -echo "" -echo " 5. After confirming everything works:" -echo " Stop HA NPM add-on" -echo "" -echo " Docker management:" -echo " pct exec ${CT_ID} -- docker compose -f /opt/npm/docker-compose.yml logs -f" -echo " pct exec ${CT_ID} -- docker compose -f /opt/npm/docker-compose.yml restart" +echo " NEXT STEPS:" +echo " 1. Verify proxy hosts at http://${CT_IP_CLEAN}:81" +echo " 2. Update OPNsense port forwards (80/443):" +echo " OLD: 10.0.0.55 -> NEW: ${CT_IP_CLEAN}" +echo " 3. If certs didn't migrate, re-request via NPM" +echo " 4. Stop HA NPM add-on after confirming" echo "============================================"