fix: bake in AppArmor unconfined for Docker-in-LXC, use HA_TOKEN env var
This commit is contained in:
+105
-144
@@ -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} <path>/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 "============================================"
|
||||
|
||||
Reference in New Issue
Block a user