diff --git a/chromium-setup/emmc-provisioning/README.md b/chromium-setup/emmc-provisioning/README.md index bc2e0e4..dddc7e3 100644 --- a/chromium-setup/emmc-provisioning/README.md +++ b/chromium-setup/emmc-provisioning/README.md @@ -18,7 +18,7 @@ emmc-provisioning/ │ ├── cm4-flash-trigger.sh Called by udev; starts the flash job │ └── 90-cm4-boot-mode.rules udev rule (USB vendor 2b8e) ├── scripts/ Deployment and one-off scripts -│ ├── deploy-to-proxmox.sh Deploy to Proxmox host + LXC 201 +│ ├── deploy-to-proxmox.sh One-command deploy to Proxmox host + LXC (by hostname) │ └── install-usbboot-on-host.sh Build and install rpiboot on the host ├── dashboard/ Flask web UI (runs in LXC or standalone) │ ├── app.py diff --git a/chromium-setup/emmc-provisioning/docs/PROXMOX-LXC-DEPLOYMENT.md b/chromium-setup/emmc-provisioning/docs/PROXMOX-LXC-DEPLOYMENT.md index 68e9893..633e05c 100644 --- a/chromium-setup/emmc-provisioning/docs/PROXMOX-LXC-DEPLOYMENT.md +++ b/chromium-setup/emmc-provisioning/docs/PROXMOX-LXC-DEPLOYMENT.md @@ -1,38 +1,53 @@ # CM4 eMMC provisioning on Proxmox (LXC + host) -The auto-flash **runs on the Proxmox host** (where the USB device appears). The **LXC** holds the same scripts and shares the **golden image** directory with the host so you can manage the image from the container. +The auto-flash **runs on the Proxmox host** (where the USB device appears). The **LXC** holds the dashboard and shares the **golden image** directory with the host. + +## One-command deploy + +From your repo, a single run deploys **all** host and LXC files (scripts, systemd units, udev, dashboard): + +```bash +./chromium-setup/emmc-provisioning/scripts/deploy-to-proxmox.sh root@YOUR_PROXMOX_HOST +``` + +Optional env: `CM4_BACKUPS_HOST_PATH=/path`, `DEPLOY_ROOTFS_STORAGE=local-lvm`, `DEPLOY_LXC_ROOT_PASSWORD=secret` (set root password in LXC and enable SSH), `DEPLOY_LXC_SSH_KEY=/path/to/pub` (default: `~/.ssh/id_ed25519.pub` or `id_rsa.pub` — copied to LXC root so you can `ssh root@`). + +The script **finds the container by hostname `cm4-provisioning`** (any existing ID). If none exists, it **creates a new LXC with the next available ID**. So you can redeploy repeatedly without assuming a fixed ID like 201. ## What is deployed | Where | What | |-------|-----| -| **Proxmox host** | udev rule, trigger script, flash script, rpiboot (after you run the install script), `/var/lib/cm4-provisioning/` (golden image dir), `/etc/cm4-provisioning/enabled` | -| **LXC 201 (cm4-provisioning)** | Same scripts in `/opt/cm4-provisioning/`, same env; `/var/lib/cm4-provisioning/` is a **bind mount** from the host (shared storage for the golden image) | +| **Proxmox host** | udev rule, trigger script, flash script, build-cloudinit and run-shrink scripts, systemd path units (build + shrink), `/var/lib/cm4-provisioning/`, `/etc/cm4-provisioning/enabled` | +| **LXC (hostname cm4-provisioning)** | Dashboard (Flask) in `/opt/cm4-provisioning/dashboard/`; `/var/lib/cm4-provisioning/` is a **bind mount** from the host (shared storage for golden image and backups) | When you plug the reTerminal in boot mode into the **host**, udev on the host runs the flash (rpiboot + dd). The golden image is read from `/var/lib/cm4-provisioning/golden.img` on the host (same path visible in the LXC). --- -## Deployment that was done +## Deployment layout (after running the deploy script) -1. **LXC 201** created on Proxmox `10.130.60.224`: - - Hostname: `cm4-provisioning` +1. **LXC** (hostname `cm4-provisioning`, ID = found by hostname or next free): - Debian 12, 1 GB RAM, 8 GB rootfs - Bind mount: host `/var/lib/cm4-provisioning` → container `/var/lib/cm4-provisioning` + - Optional second mount: `CM4_BACKUPS_HOST_PATH` → container `/var/lib/cm4-provisioning/backups` 2. **On the host**: - `/opt/cm4-provisioning/flash-emmc-on-connect.sh` – flash script + - `/opt/cm4-provisioning/build-cloudinit-image.sh` – build cloud-init image (triggered by path unit) + - `/opt/cm4-provisioning/run-shrink-on-host.sh` – PiShrink for dashboard Shrink/Compress - `/usr/local/bin/cm4-flash-trigger.sh` – started by udev - `/etc/udev/rules.d/90-cm4-boot-mode.rules` – run trigger when USB vendor `2b8e` is added - - `/opt/cm4-provisioning/env` – `GOLDEN_IMAGE`, `RPIBOOT_DIR`, `EMMC_SIZE_BYTES` + - `/opt/cm4-provisioning/env` – `GOLDEN_IMAGE`, `RPIBOOT_DIR`, `EMMC_SIZE_BYTES` (and `BACKUPS_DIR` if `CM4_BACKUPS_HOST_PATH` set) - `/etc/cm4-provisioning/enabled` – safety switch (remove to disable auto-flash) + - systemd: `cm4-build-cloudinit.path` + `.service`, `cm4-shrink.path` + `.service` + - `/opt/cm4-provisioning/fix-gadget-bootcode-on-host.sh` – used by `install-usbboot-on-host.sh` after building usbboot (fixes "rpiboot gadget empty" when gadget has broken symlinks) -3. **Inside LXC 201**: - - Same scripts in `/opt/cm4-provisioning/` and env (for reference/backup) - - Golden image path: `/var/lib/cm4-provisioning/golden.img` (bind-mounted from host) - - **Dashboard** (optional): Flask app in `/opt/cm4-provisioning/dashboard/` to monitor deployment and show connection steps; see below. +3. **Inside the LXC** (use `pct exec -- ...` where `` is the ID of the container with hostname `cm4-provisioning`; get it with `pct list`): + - Dashboard: Flask app in `/opt/cm4-provisioning/dashboard/` (monitor deployment, backup list, build cloud-init, set golden). + - Golden image path: `/var/lib/cm4-provisioning/golden.img` (bind-mounted from host). -4. **usbboot (rpiboot)** was **not** built on the host (no outbound DNS during deploy). You must install it when the host has internet. +4. **usbboot (rpiboot)** is **not** installed by the deploy script. Install it when the host has internet (see below). --- @@ -55,11 +70,13 @@ ssh root@10.130.60.224 bash /tmp/emmc-provisioning-deploy/scripts/install-usbboot-on-host.sh ``` -This installs dependencies, clones usbboot, builds it, and copies `rpiboot` to `/opt/usbboot/`. +This installs dependencies, clones usbboot, builds it, and copies `rpiboot` to `/opt/usbboot/`. It then runs **fix-gadget-bootcode-on-host.sh** if present (from deploy), so the gadget has valid boot files and rpiboot does not fail with "No bootcode files found". -### 2. Enable root SSH and add your SSH key to LXC 201 +### 2. Enable root SSH and add your SSH key to the LXC -No root password is set by default. To log in as root over SSH: +If you deployed with **`DEPLOY_LXC_ROOT_PASSWORD`** and/or a default SSH key (**`~/.ssh/id_ed25519.pub`** or **`id_rsa.pub`**), the LXC already has SSH enabled, root password set, and your key in `/root/.ssh/authorized_keys` — you can **skip** to `ssh root@` (get IP from deploy output or `pct exec -- hostname -I`). + +Otherwise, to enable root SSH and add a key: - **Option A – Use the setup script (recommended):** From your machine (with SSH key and optional password): @@ -71,11 +88,11 @@ No root password is set by default. To log in as root over SSH: ROOT_PASSWORD='YourPassword' ./chromium-setup/emmc-provisioning/scripts/setup-lxc-ssh.sh root@10.130.60.224 ~/.ssh/id_ed25519.pub ``` - Then connect with `ssh root@` (script prints the IP). Get the IP anytime with: - `ssh root@10.130.60.224 "pct exec 201 -- hostname -I"` + Then connect with `ssh root@` (script prints the IP). To get the LXC IP: + `ssh root@HOST "CID=\$(pct list -no-header -output vmid,name | awk '\''\$2==\"cm4-provisioning\"{print \$1}'\''); pct exec \$CID -- hostname -I"` - **Option B – Manual:** - `ssh root@10.130.60.224` then `pct exec 201 -- bash` to get a shell in the container. Run `apt-get install -y openssh-server`, edit `/etc/ssh/sshd_config` to set `PermitRootLogin yes`, run `passwd` to set root password, add your key to `/root/.ssh/authorized_keys`, and restart `ssh`. + `ssh root@HOST`, then `pct exec -- bash` (use the container ID from `pct list` for hostname cm4-provisioning). Install openssh-server, set `PermitRootLogin yes`, set root password, add your key, restart ssh. ### 3. (Optional) Store backup images on a host directory @@ -87,7 +104,7 @@ To keep backup images on a specific host path (e.g. a large disk or NFS mount) i CM4_BACKUPS_HOST_PATH=/mnt/storage/cm4-backups ./chromium-setup/emmc-provisioning/scripts/deploy-to-proxmox.sh root@10.130.60.224 ``` -Create `/mnt/storage/cm4-backups` (or your path) on the host first if it doesn’t exist; the deploy script will create it if possible. To add or change the backup mount on an already-deployed host, set `CM4_BACKUPS_HOST_PATH` and run the deploy script again, then on the host add `BACKUPS_DIR=` to `/opt/cm4-provisioning/env` and add the bind mount (see deploy script for the `pct set 201 -mp1 ...` step). +Create `/mnt/storage/cm4-backups` (or your path) on the host first if it doesn’t exist; the deploy script will create it if possible. To add or change the backup mount, set `CM4_BACKUPS_HOST_PATH` and run the deploy script again (it reuses the container by hostname and updates the bind mount). ### 4. Put the golden image on the host (or in the LXC) @@ -100,23 +117,23 @@ The image must be at **`/var/lib/cm4-provisioning/golden.img`** on the **host**. - **From the LXC** (e.g. after copying the image into the container elsewhere first): ```bash - pct exec 201 -- ls -la /var/lib/cm4-provisioning/ - # Copy to that path inside the container; it's the same as the host path. + pct exec -- ls -la /var/lib/cm4-provisioning/ + # Replace with the ID of the cm4-provisioning container (pct list). ``` ### 5. Run the provisioning dashboard (optional) The dashboard shows **connection steps** and **live deployment status** (idle / connecting / flashing / done / error) and a recent flash log. It reads the same `status.json` and `flash.log` that the host’s flash script writes (via the bind-mounted `/var/lib/cm4-provisioning`). -**Inside LXC 201:** +**Inside the LXC (pct exec -- bash):** ```bash # Copy dashboard into the container (from host, if you have the repo there) # Or from your workstation: # rsync -a chromium-setup/emmc-provisioning/dashboard/ root@10.130.60.224:/tmp/dashboard/ -# ssh root@10.130.60.224 "pct push 201 /tmp/dashboard/app.py /opt/cm4-provisioning/dashboard/ && pct push 201 /tmp/dashboard/cm4-dashboard.service /opt/cm4-provisioning/dashboard/ && pct exec 201 -- mkdir -p /opt/cm4-provisioning/dashboard/templates && ..." +# Or re-run deploy-to-proxmox.sh to push the latest dashboard files. -# Inside the LXC (pct exec 201 -- bash): +# Inside the LXC (pct exec -- bash): apt-get update && apt-get install -y python3-flask mkdir -p /opt/cm4-provisioning/dashboard/templates # Copy app.py, templates/index.html, cm4-dashboard.service into the container (see dashboard/README.md) @@ -126,7 +143,7 @@ systemctl daemon-reload systemctl enable --now cm4-dashboard ``` -Then open **http://<LXC-201-IP>:5000** (get the IP with `pct exec 201 -- hostname -I`). If the LXC is on a private network, set up port forwarding on the Proxmox host or use a reverse proxy so you can reach the dashboard from your browser. +Then open **http://<LXC-IP>:5000** (get the IP with `pct exec -- hostname -I`). If the LXC is on a private network, set up port forwarding on the Proxmox host or use a reverse proxy. ### 6. Optional: disable or enable auto-flash @@ -221,7 +238,7 @@ That script syncs the repo to the host and reinstalls scripts on both the host a | Item | Location | |------|----------| -| LXC | 201, hostname `cm4-provisioning`, Proxmox `10.130.60.224` | +| LXC | Hostname `cm4-provisioning` (ID from `pct list`), on your Proxmox host | | Golden image | `/var/lib/cm4-provisioning/golden.img` (host and LXC see the same file) | | Flash runs on | Proxmox **host** (udev + rpiboot + dd) | | Build rpiboot on host | Run `scripts/install-usbboot-on-host.sh` on the host when it has internet | diff --git a/chromium-setup/emmc-provisioning/scripts/deploy-to-proxmox.sh b/chromium-setup/emmc-provisioning/scripts/deploy-to-proxmox.sh index 0eea037..1c50238 100755 --- a/chromium-setup/emmc-provisioning/scripts/deploy-to-proxmox.sh +++ b/chromium-setup/emmc-provisioning/scripts/deploy-to-proxmox.sh @@ -1,11 +1,22 @@ #!/usr/bin/env bash -# Deploy CM4 eMMC provisioning to a Proxmox host (creates LXC 201, installs scripts on host and in LXC). +# Deploy CM4 eMMC provisioning to a Proxmox host: host scripts, udev, systemd units, +# LXC container (dashboard), usbboot (rpiboot), and PiShrink. Uses hostname "cm4-provisioning" +# to find the container on redeploy; creates with next available ID if not found. +# +# With host internet: installs usbboot and PiShrink so USB flash/backup and dashboard +# Shrink/Compress work. The only manual step left is to add a golden image for Deploy. +# # Usage: ./deploy-to-proxmox.sh [proxmox_host] -# Example: ./deploy-to-proxmox.sh root@10.130.60.224 -# Optional: DEPLOY_ROOTFS_STORAGE=local-lvm (or local-zfs, etc.) — storage for LXC rootfs -# Optional: CM4_BACKUPS_HOST_PATH=/mnt/storage/cm4-backups — host dir for backup images; bind-mounted into LXC so images are stored on the host -# Requires: ssh key access to root@ -# Logging: set DEPLOY_LOG=1 to also write to deploy-YYYYMMDD-HHMMSS.log in the script dir. +# Example: ./deploy-to-proxmox.sh root@10.20.30.152 +# +# Optional env: +# DEPLOY_ROOTFS_STORAGE=local-lvm — LXC rootfs storage (default: local-lvm) +# CM4_BACKUPS_HOST_PATH=/path — host dir for backups; bind-mounted into LXC +# DEPLOY_LXC_ROOT_PASSWORD=secret — set root password in LXC and enable SSH +# DEPLOY_LXC_SSH_KEY=/path/to/pub — copy this key to LXC root (default: ~/.ssh/id_ed25519.pub or id_rsa.pub) +# DEPLOY_LOG=1 — also log to deploy-YYYYMMDD-HHMMSS.log +# +# Requires: ssh key access to root@. For full install (usbboot, PiShrink), host needs internet. set -e PROXMOX="${1:-root@10.130.60.224}" @@ -21,24 +32,56 @@ fi log() { echo "[$(date -Iseconds)] $*"; } -log "Deploying to $PROXMOX ..." -# Use a clean staging dir (remove if present so we never write into a symlink or bad state) -log "[1/4] Cleaning remote staging dir ..." -ssh "$PROXMOX" "rm -rf /tmp/emmc-provisioning-deploy" -log "[2/4] Rsync repo to $PROXMOX ..." -rsync -a "$REPO_DIR/" "$PROXMOX:/tmp/emmc-provisioning-deploy/" --exclude='.git' --exclude='scripts/deploy-to-proxmox.sh' -log "[3/4] Running remote install (host + LXC) ..." +# Optional: gather SSH key and LXC root password for setup inside deploy +DEPLOY_SSH_KEY_B64="" +DEPLOY_LXC_PWD_B64="" +if [[ -n "${DEPLOY_LXC_ROOT_PASSWORD:-}" ]]; then + DEPLOY_LXC_PWD_B64=$(echo -n "$DEPLOY_LXC_ROOT_PASSWORD" | base64 -w 0 2>/dev/null || base64 2>/dev/null | tr -d '\n') +fi +KEY_FILE="${DEPLOY_LXC_SSH_KEY:-}" +if [[ -z "$KEY_FILE" ]]; then + for f in ~/.ssh/id_ed25519.pub ~/.ssh/id_rsa.pub; do + [[ -f "$f" ]] && { KEY_FILE="$f"; break; } + done +fi +if [[ -n "$KEY_FILE" && -f "$KEY_FILE" ]]; then + DEPLOY_SSH_KEY_B64=$(base64 -w 0 < "$KEY_FILE" 2>/dev/null || base64 < "$KEY_FILE" 2>/dev/null | tr -d '\n') + log "Will copy SSH key to LXC: $KEY_FILE" +fi +[[ -n "$DEPLOY_LXC_ROOT_PASSWORD" ]] && log "Will set LXC root password (DEPLOY_LXC_ROOT_PASSWORD)." -ssh "$PROXMOX" "ROOTFS_STORAGE='$ROOTFS_STORAGE' CM4_BACKUPS_HOST_PATH='${CM4_BACKUPS_HOST_PATH:-}'" bash -s << 'REMOTE' +log "Deploying to $PROXMOX ..." +log "[1/5] Cleaning remote staging dir ..." +ssh "$PROXMOX" "rm -rf /tmp/emmc-provisioning-deploy" +log "[2/5] Rsync repo to $PROXMOX ..." +rsync -a "$REPO_DIR/" "$PROXMOX:/tmp/emmc-provisioning-deploy/" --exclude='.git' --exclude='scripts/deploy-to-proxmox.sh' --exclude='scripts/deploy-*.log' +log "[3/5] Running remote install (host + LXC) ..." + +# Pass optional LXC SSH vars (base64) so remote can set password and add key +ssh "$PROXMOX" "ROOTFS_STORAGE='$ROOTFS_STORAGE' CM4_BACKUPS_HOST_PATH='${CM4_BACKUPS_HOST_PATH:-}' DEPLOY_SSH_KEY_B64='${DEPLOY_SSH_KEY_B64:-}' DEPLOY_LXC_PWD_B64='${DEPLOY_LXC_PWD_B64:-}'" bash -s << 'REMOTE' set -e DEPLOY=/tmp/emmc-provisioning-deploy ROOTFS_STORAGE="${ROOTFS_STORAGE:-local-lvm}" BACKUPS_HOST_PATH="${CM4_BACKUPS_HOST_PATH:-}" +LXC_HOSTNAME="cm4-provisioning" log() { echo "[$(date -Iseconds)] $*"; } -# Ensure LXC 201 exists (create if not) -if ! pct status 201 &>/dev/null; then - # Use Debian 12 template: prefer one in cache, else download latest +# --- Find existing LXC by hostname or use next available ID --- +CTID="" +for id in $(pct list 2>/dev/null | awk 'NR>1 {print $1}'); do + h=$(pct config "$id" 2>/dev/null | sed -n 's/^hostname: *//p') + if [[ "$h" == "$LXC_HOSTNAME" ]]; then + CTID="$id" + break + fi +done +if [[ -n "$CTID" ]]; then + log "Found existing LXC $CTID (hostname: $LXC_HOSTNAME)." +else + MAX_ID=$(pct list 2>/dev/null | awk 'NR>1 {print $1}' | sort -n | tail -1) + [[ -z "$MAX_ID" ]] && MAX_ID=0 + CTID=$((MAX_ID + 1)) + log "Creating LXC $CTID ($LXC_HOSTNAME) (rootfs on ${ROOTFS_STORAGE})..." VZTMPL_DIR=/var/lib/vz/template/cache DEBIAN12_TMPL=$(ls "$VZTMPL_DIR"/debian-12-standard_*.tar.zst 2>/dev/null | head -1) if [[ -z "$DEBIAN12_TMPL" ]]; then @@ -46,30 +89,28 @@ if ! pct status 201 &>/dev/null; then pveam download local debian-12-standard_12.12-1_amd64.tar.zst || pveam download local debian-12-standard_12.7-1_amd64.tar.zst DEBIAN12_TMPL=$(ls "$VZTMPL_DIR"/debian-12-standard_*.tar.zst 2>/dev/null | head -1) fi + [[ -z "$DEBIAN12_TMPL" ]] && { log "Error: no Debian 12 template found"; exit 1; } TMPL_NAME=$(basename "$DEBIAN12_TMPL") - log "Creating LXC 201 (cm4-provisioning) (rootfs on ${ROOTFS_STORAGE}, template ${TMPL_NAME})..." - pct create 201 "local:vztmpl/${TMPL_NAME}" \ - --hostname cm4-provisioning --memory 1024 --swap 0 --cores 1 \ + pct create "$CTID" "local:vztmpl/${TMPL_NAME}" \ + --hostname "$LXC_HOSTNAME" --memory 1024 --swap 0 --cores 1 \ --rootfs "${ROOTFS_STORAGE}:8" --net0 name=eth0,bridge=vmbr0,ip=dhcp \ --unprivileged 0 --features nesting=1 -tag cm4-provisioning mkdir -p /var/lib/cm4-provisioning - pct set 201 -mp0 /var/lib/cm4-provisioning,mp=/var/lib/cm4-provisioning - log "LXC 201 created and mount configured." -else - log "LXC 201 already exists." + pct set "$CTID" -mp0 /var/lib/cm4-provisioning,mp=/var/lib/cm4-provisioning + log "LXC $CTID created and mount configured." fi -# Optional: bind-mount a host directory for backup images (so they are stored on the host, not LXC rootfs) +# Optional: bind-mount host directory for backup images if [[ -n "$BACKUPS_HOST_PATH" ]]; then mkdir -p "$BACKUPS_HOST_PATH" - pct stop 201 2>/dev/null || true - pct set 201 -mp1 "$BACKUPS_HOST_PATH",mp=/var/lib/cm4-provisioning/backups - pct start 201 2>/dev/null || true - log "Backups mount: host $BACKUPS_HOST_PATH -> LXC /var/lib/cm4-provisioning/backups" + pct stop "$CTID" 2>/dev/null || true + pct set "$CTID" -mp1 "$BACKUPS_HOST_PATH",mp=/var/lib/cm4-provisioning/backups + pct start "$CTID" 2>/dev/null || true + log "Backups mount: host $BACKUPS_HOST_PATH -> LXC $CTID /var/lib/cm4-provisioning/backups" fi -# Host: install scripts and udev (from host/) -log "Host: installing scripts and udev ..." +# --- Host: scripts, systemd, udev --- +log "Host: installing scripts and systemd units ..." mkdir -p /opt/cm4-provisioning /etc/cm4-provisioning cp "$DEPLOY/host/flash-emmc-on-connect.sh" /opt/cm4-provisioning/ chmod +x /opt/cm4-provisioning/flash-emmc-on-connect.sh @@ -77,6 +118,7 @@ cp "$DEPLOY/host/build-cloudinit-image.sh" /opt/cm4-provisioning/ chmod +x /opt/cm4-provisioning/build-cloudinit-image.sh cp "$DEPLOY/host/run-shrink-on-host.sh" /opt/cm4-provisioning/ chmod +x /opt/cm4-provisioning/run-shrink-on-host.sh +cp "$DEPLOY/scripts/fix-gadget-bootcode-on-host.sh" /opt/cm4-provisioning/ 2>/dev/null && chmod +x /opt/cm4-provisioning/fix-gadget-bootcode-on-host.sh || true cp "$DEPLOY/host/cm4-flash-trigger.sh" /usr/local/bin/ chmod +x /usr/local/bin/cm4-flash-trigger.sh cp "$DEPLOY/host/cm4-flash.service" /etc/systemd/system/ @@ -89,7 +131,8 @@ systemctl enable --now cm4-build-cloudinit.path 2>/dev/null || true systemctl enable --now cm4-shrink.path 2>/dev/null || true cp "$DEPLOY/host/89-cm4-boot-mode-permissions.rules" /etc/udev/rules.d/ 2>/dev/null || true cp "$DEPLOY/host/90-cm4-boot-mode.rules" /etc/udev/rules.d/ -udevadm control --reload-rules +udevadm control --reload-rules 2>/dev/null || true + log "Host: env and dirs ..." cat > /opt/cm4-provisioning/env << 'ENV' GOLDEN_IMAGE=/var/lib/cm4-provisioning/golden.img @@ -101,44 +144,86 @@ touch /etc/cm4-provisioning/enabled mkdir -p /var/lib/cm4-provisioning/backups [[ -n "$BACKUPS_HOST_PATH" ]] && mkdir -p "$BACKUPS_HOST_PATH" -# Start LXC if stopped -log "Starting LXC 201 if stopped ..." -pct start 201 2>/dev/null || true +# --- Host: install usbboot (rpiboot) so USB flash/backup works --- +log "Host: installing usbboot (rpiboot)..." +if bash "$DEPLOY/scripts/install-usbboot-on-host.sh" 2>&1; then + log "Host: usbboot installed at /opt/usbboot/rpiboot" +else + log "Warning: usbboot install failed (e.g. no internet). USB flash/backup will not work until you run: bash /tmp/emmc-provisioning-deploy/scripts/install-usbboot-on-host.sh" +fi -# LXC: install scripts (from host/) +# --- Host: install PiShrink so dashboard Shrink/Compress work --- +log "Host: installing PiShrink..." +if bash "$DEPLOY/scripts/install-pishrink-on-host.sh" 2>&1; then + log "Host: PiShrink installed" + grep -q "SHRINK_BACKUP" /opt/cm4-provisioning/env || echo "SHRINK_BACKUP=1" >> /opt/cm4-provisioning/env + grep -q "PISHRINK_COMPRESS" /opt/cm4-provisioning/env || echo "PISHRINK_COMPRESS=xz" >> /opt/cm4-provisioning/env +else + log "Warning: PiShrink install failed (e.g. no internet). Dashboard Shrink/Compress will report 'PiShrink not installed' until you run: bash /tmp/emmc-provisioning-deploy/scripts/install-pishrink-on-host.sh" +fi + +# --- Start LXC if stopped --- +log "Starting LXC $CTID if stopped ..." +pct start "$CTID" 2>/dev/null || true + +# --- LXC: flash scripts (for reference; actual flash runs on host) --- log "LXC: installing flash scripts ..." -pct exec 201 -- mkdir -p /opt/cm4-provisioning /etc/cm4-provisioning -pct push 201 "$DEPLOY/host/flash-emmc-on-connect.sh" /opt/cm4-provisioning/flash-emmc-on-connect.sh -pct exec 201 -- chmod +x /opt/cm4-provisioning/flash-emmc-on-connect.sh -pct push 201 "$DEPLOY/host/cm4-flash-trigger.sh" /usr/local/bin/cm4-flash-trigger.sh -pct exec 201 -- chmod +x /usr/local/bin/cm4-flash-trigger.sh -pct exec 201 -- bash -c 'echo -e "GOLDEN_IMAGE=/var/lib/cm4-provisioning/golden.img\nRPIBOOT_DIR=/opt/usbboot\nEMMC_SIZE_BYTES=8589934592" > /opt/cm4-provisioning/env' +pct exec "$CTID" -- mkdir -p /opt/cm4-provisioning /etc/cm4-provisioning +pct push "$CTID" "$DEPLOY/host/flash-emmc-on-connect.sh" /opt/cm4-provisioning/flash-emmc-on-connect.sh +pct exec "$CTID" -- chmod +x /opt/cm4-provisioning/flash-emmc-on-connect.sh +pct push "$CTID" "$DEPLOY/host/cm4-flash-trigger.sh" /usr/local/bin/cm4-flash-trigger.sh +pct exec "$CTID" -- chmod +x /usr/local/bin/cm4-flash-trigger.sh +pct exec "$CTID" -- bash -c 'echo -e "GOLDEN_IMAGE=/var/lib/cm4-provisioning/golden.img\nRPIBOOT_DIR=/opt/usbboot\nEMMC_SIZE_BYTES=8589934592" > /opt/cm4-provisioning/env' -# LXC: install dashboard +# --- LXC: dashboard (all files) --- log "LXC: installing dashboard ..." -pct exec 201 -- mkdir -p /opt/cm4-provisioning/dashboard/templates -pct push 201 "$DEPLOY/dashboard/app.py" /opt/cm4-provisioning/dashboard/app.py -pct push 201 "$DEPLOY/dashboard/templates/index.html" /opt/cm4-provisioning/dashboard/templates/index.html -pct push 201 "$DEPLOY/dashboard/cm4-dashboard.service" /opt/cm4-provisioning/dashboard/cm4-dashboard.service +pct exec "$CTID" -- mkdir -p /opt/cm4-provisioning/dashboard/templates +pct push "$CTID" "$DEPLOY/dashboard/app.py" /opt/cm4-provisioning/dashboard/app.py +pct push "$CTID" "$DEPLOY/dashboard/templates/index.html" /opt/cm4-provisioning/dashboard/templates/index.html +pct push "$CTID" "$DEPLOY/dashboard/cm4-dashboard.service" /opt/cm4-provisioning/dashboard/cm4-dashboard.service -# LXC: install Flask and enable dashboard service -log "LXC: installing python3-flask and enabling cm4-dashboard service ..." -pct exec 201 -- bash -c 'apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq python3-flask' -pct exec 201 -- cp /opt/cm4-provisioning/dashboard/cm4-dashboard.service /etc/systemd/system/ -pct exec 201 -- systemctl daemon-reload -pct exec 201 -- systemctl enable --now cm4-dashboard +# --- LXC: Flask and systemd --- +log "LXC: installing python3-flask and enabling cm4-dashboard ..." +pct exec "$CTID" -- bash -c 'apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq python3-flask' +pct exec "$CTID" -- cp /opt/cm4-provisioning/dashboard/cm4-dashboard.service /etc/systemd/system/ +pct exec "$CTID" -- systemctl daemon-reload +pct exec "$CTID" -- systemctl enable --now cm4-dashboard log "LXC: cm4-dashboard enabled and started." -log "Deploy done (remote)." -echo "Next: Install usbboot on host when online: ssh 'bash /tmp/emmc-provisioning-deploy/scripts/install-usbboot-on-host.sh'" +# --- LXC: optional SSH (root password + SSH key from deploy env) --- +if [[ -n "${DEPLOY_SSH_KEY_B64:-}" ]] || [[ -n "${DEPLOY_LXC_PWD_B64:-}" ]]; then + log "LXC: configuring SSH (root login, password, authorized_keys)..." + pct exec "$CTID" -- bash -c 'apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq openssh-server 2>/dev/null; systemctl enable ssh 2>/dev/null; systemctl start ssh 2>/dev/null' || true + pct exec "$CTID" -- bash -c 'sed -i "s/^#*PermitRootLogin.*/PermitRootLogin yes/" /etc/ssh/sshd_config 2>/dev/null; grep -q "^PermitRootLogin" /etc/ssh/sshd_config || echo "PermitRootLogin yes" >> /etc/ssh/sshd_config; systemctl restart ssh 2>/dev/null || systemctl restart sshd 2>/dev/null' || true + if [[ -n "${DEPLOY_LXC_PWD_B64:-}" ]]; then + PWD_RAW=$(echo "$DEPLOY_LXC_PWD_B64" | base64 -d 2>/dev/null) + echo "root:$PWD_RAW" | pct exec "$CTID" -- chpasswd 2>/dev/null && log "LXC: root password set." || true + fi + if [[ -n "${DEPLOY_SSH_KEY_B64:-}" ]]; then + echo "$DEPLOY_SSH_KEY_B64" | base64 -d 2>/dev/null | pct exec "$CTID" -- bash -c "mkdir -p /root/.ssh; chmod 700 /root/.ssh; cat >> /root/.ssh/authorized_keys; chmod 600 /root/.ssh/authorized_keys" 2>/dev/null && log "LXC: SSH key added to /root/.ssh/authorized_keys." || true + fi + LXC_IP=$(pct exec "$CTID" -- hostname -I 2>/dev/null | awk '{print $1}') + [[ -n "$LXC_IP" ]] && echo "LXC SSH: ssh root@$LXC_IP" +fi + +log "Deploy done (remote). LXC ID: $CTID" REMOTE -log "[4/4] Deploy finished." +log "[5/5] Deploy finished." echo "" -echo "Done. Put golden.img in /var/lib/cm4-provisioning/ on the host (or scp to LXC 201 at /var/lib/cm4-provisioning/)." -[[ -n "${CM4_BACKUPS_HOST_PATH:-}" ]] && echo "Backup images are stored on the host at: $CM4_BACKUPS_HOST_PATH (bind-mounted into LXC at /var/lib/cm4-provisioning/backups)." -echo "When the host has internet, run on the host: bash /tmp/emmc-provisioning-deploy/scripts/install-usbboot-on-host.sh" -echo "Dashboard: install flask in LXC 201 and enable cm4-dashboard.service (see docs/PROXMOX-LXC-DEPLOYMENT.md)." +echo "=== Deploy complete ===" +echo "Host and LXC are fully set up: usbboot (rpiboot), PiShrink, dashboard, systemd, udev." +echo "" +echo "--- Only remaining step (manual) ---" +echo " Add a golden image for Deploy (writing image to device):" +echo " • Dashboard: open http://:5000 → Build cloud-init image → then Set as golden" +echo " • Or copy your image: scp your-image.img $PROXMOX:/var/lib/cm4-provisioning/golden.img" +echo " Backup (read from device) works without golden.img." +echo "" +echo "--- You have ---" +echo " - Dashboard: http://:5000 (LXC IP: from Proxmox UI, or on host: pct exec -- hostname -I)" +[[ -n "${DEPLOY_LXC_ROOT_PASSWORD:-}" || -n "${DEPLOY_SSH_KEY_B64:-}" ]] && echo " - LXC SSH: ssh root@ (password and/or key were set)" +[[ -n "${CM4_BACKUPS_HOST_PATH:-}" ]] && echo " - Backups on host: $CM4_BACKUPS_HOST_PATH" if [[ -n "$LOG_FILE" ]]; then - echo "Log written to: $LOG_FILE" + echo " - Log: $LOG_FILE" fi diff --git a/chromium-setup/emmc-provisioning/scripts/install-usbboot-on-host.sh b/chromium-setup/emmc-provisioning/scripts/install-usbboot-on-host.sh index bedcb9a..5ea09ae 100644 --- a/chromium-setup/emmc-provisioning/scripts/install-usbboot-on-host.sh +++ b/chromium-setup/emmc-provisioning/scripts/install-usbboot-on-host.sh @@ -18,3 +18,13 @@ for dir in mass-storage-gadget64 mass-storage-gadget; do [[ -d "$dir" ]] && cp -a "$dir" /opt/usbboot/ done echo "usbboot installed at /opt/usbboot/rpiboot" + +# If rpiboot later fails with "No bootcode files" (broken symlinks in gadget), fix-gadget repairs it. +# Run it now; prefer copy in /opt (from deploy) or in deploy dir. +for fix in /opt/cm4-provisioning/fix-gadget-bootcode-on-host.sh /tmp/emmc-provisioning-deploy/scripts/fix-gadget-bootcode-on-host.sh; do + if [[ -f "$fix" ]]; then + echo "Running fix-gadget-bootcode-on-host.sh to ensure gadget has valid boot files..." + bash "$fix" || true + break + fi +done diff --git a/chromium-setup/emmc-provisioning/scripts/setup-lxc-ssh.sh b/chromium-setup/emmc-provisioning/scripts/setup-lxc-ssh.sh index c0163e9..af50e32 100755 --- a/chromium-setup/emmc-provisioning/scripts/setup-lxc-ssh.sh +++ b/chromium-setup/emmc-provisioning/scripts/setup-lxc-ssh.sh @@ -1,20 +1,17 @@ #!/usr/bin/env bash -# Enable root SSH login on LXC 201 (cm4-provisioning) and add your SSH key. +# Enable root SSH login on the cm4-provisioning LXC and add your SSH key. +# Finds the container by hostname "cm4-provisioning" on the host, or use CTID=id to override. # Usage: # ./setup-lxc-ssh.sh [proxmox_host] [ssh_public_key_file] # ROOT_PASSWORD='yourpassword' ./setup-lxc-ssh.sh [proxmox_host] [ssh_public_key_file] -# -# Examples: -# ./setup-lxc-ssh.sh root@10.130.60.224 -# ./setup-lxc-ssh.sh root@10.130.60.224 ~/.ssh/id_ed25519.pub -# ROOT_PASSWORD='MySecurePass' ./setup-lxc-ssh.sh root@10.130.60.224 +# CTID=202 ./setup-lxc-ssh.sh root@10.130.60.224 # force a specific container ID # # If ssh_public_key_file is omitted, uses ~/.ssh/id_ed25519.pub or ~/.ssh/id_rsa.pub. set -e PROXMOX="${1:-root@10.130.60.224}" KEY_FILE="${2:-}" -CTID="${CTID:-201}" +CTID="${CTID:-}" # Find public key if [[ -z "$KEY_FILE" ]]; then @@ -34,39 +31,45 @@ KEY_CONTENT=$(cat "$KEY_FILE") ROOT_PASSWORD="${ROOT_PASSWORD:-}" echo "Using key from: $KEY_FILE" -echo "Configuring LXC $CTID on $PROXMOX (enable SSH, root login, add key)..." +echo "Configuring LXC (cm4-provisioning) on $PROXMOX (enable SSH, root login, add key)..." -ssh "$PROXMOX" bash -s << REMOTE +ssh "$PROXMOX" "CTID='$CTID' KEY_CONTENT='$(echo "$KEY_CONTENT" | sed "s/'/'\\\\''/g")' ROOT_PASSWORD='$(echo "$ROOT_PASSWORD" | sed "s/'/'\\\\''/g")'" bash -s << 'REMOTE' set -e -CTID="$CTID" -KEY_CONTENT='$(echo "$KEY_CONTENT" | sed "s/'/'\\\\''/g")' -ROOT_PASSWORD='$(echo "$ROOT_PASSWORD" | sed "s/'/'\\\\''/g")' +# Resolve CTID by hostname if not provided +if [[ -z "$CTID" ]]; then + CTID=$(pct list -no-header -output vmid,name 2>/dev/null | awk '$2=="cm4-provisioning"{print $1}' | head -1) +fi +if [[ -z "$CTID" ]]; then + echo "Error: no container with hostname cm4-provisioning found. Set CTID=id and re-run." + exit 1 +fi +echo "Using LXC ID: $CTID" # Ensure container is running -pct start \$CTID 2>/dev/null || true +pct start $CTID 2>/dev/null || true sleep 2 # Install openssh-server if missing, enable and start -pct exec \$CTID -- bash -c 'apt-get update -qq && apt-get install -y -qq openssh-server 2>/dev/null; systemctl enable ssh 2>/dev/null; systemctl start ssh 2>/dev/null' || true +pct exec $CTID -- bash -c 'apt-get update -qq && apt-get install -y -qq openssh-server 2>/dev/null; systemctl enable ssh 2>/dev/null; systemctl start ssh 2>/dev/null' || true # Enable root login via password and/or public key -pct exec \$CTID -- bash -c ' +pct exec $CTID -- bash -c ' sed -i "s/^#*PermitRootLogin.*/PermitRootLogin yes/" /etc/ssh/sshd_config 2>/dev/null || true grep -q "^PermitRootLogin" /etc/ssh/sshd_config || echo "PermitRootLogin yes" >> /etc/ssh/sshd_config systemctl restart ssh 2>/dev/null || systemctl restart sshd 2>/dev/null || true ' # Set root password if provided (pass via stdin so no quoting in -c) -if [[ -n "\$ROOT_PASSWORD" ]]; then - echo "root:\$ROOT_PASSWORD" | pct exec \$CTID -- chpasswd +if [[ -n "$ROOT_PASSWORD" ]]; then + echo "root:$ROOT_PASSWORD" | pct exec $CTID -- chpasswd echo "Root password set." fi # Add SSH key to root (pass key via stdin to avoid quoting issues) -echo "\$KEY_CONTENT" | pct exec \$CTID -- bash -c "mkdir -p /root/.ssh; chmod 700 /root/.ssh; cat >> /root/.ssh/authorized_keys; chmod 600 /root/.ssh/authorized_keys" +echo "$KEY_CONTENT" | pct exec $CTID -- bash -c "mkdir -p /root/.ssh; chmod 700 /root/.ssh; cat >> /root/.ssh/authorized_keys; chmod 600 /root/.ssh/authorized_keys" echo "SSH key added to /root/.ssh/authorized_keys" # Show IP for convenience -IP=\$(pct exec \$CTID -- hostname -I 2>/dev/null | awk '{print \$1}') -echo "Done. Connect with: ssh root@\$IP" +IP=$(pct exec $CTID -- hostname -I 2>/dev/null | awk '{print $1}') +echo "Done. Connect with: ssh root@$IP" REMOTE