Update eMMC provisioning documentation and deployment scripts: clarify one-command deploy process, enhance deployment layout details, and improve SSH setup instructions for LXC containers. Add functionality to dynamically find LXC by hostname and streamline backup directory configuration.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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@<LXC-IP>`).
|
||||
|
||||
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 <CTID> -- ...` where `<CTID>` 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@<LXC-IP>` (get IP from deploy output or `pct exec <CTID> -- 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@<LXC-IP>` (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@<LXC-IP>` (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 <CTID> -- 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=<path>` 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 <CTID> -- ls -la /var/lib/cm4-provisioning/
|
||||
# Replace <CTID> 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 <CTID> -- 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 <CTID> -- 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 <CTID> -- 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 |
|
||||
|
||||
@@ -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@<host>
|
||||
# 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@<host>. 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 <host> '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://<LXC-IP>: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://<LXC-IP>:5000 (LXC IP: from Proxmox UI, or on host: pct exec <CTID> -- hostname -I)"
|
||||
[[ -n "${DEPLOY_LXC_ROOT_PASSWORD:-}" || -n "${DEPLOY_SSH_KEY_B64:-}" ]] && echo " - LXC SSH: ssh root@<LXC-IP> (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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user