Compare commits
2 Commits
a3661df8c2
...
d12c7af664
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d12c7af664 | ||
|
|
5afb194daf |
@@ -18,7 +18,7 @@ emmc-provisioning/
|
|||||||
│ ├── cm4-flash-trigger.sh Called by udev; starts the flash job
|
│ ├── cm4-flash-trigger.sh Called by udev; starts the flash job
|
||||||
│ └── 90-cm4-boot-mode.rules udev rule (USB vendor 2b8e)
|
│ └── 90-cm4-boot-mode.rules udev rule (USB vendor 2b8e)
|
||||||
├── scripts/ Deployment and one-off scripts
|
├── 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
|
│ └── install-usbboot-on-host.sh Build and install rpiboot on the host
|
||||||
├── dashboard/ Flask web UI (runs in LXC or standalone)
|
├── dashboard/ Flask web UI (runs in LXC or standalone)
|
||||||
│ ├── app.py
|
│ ├── app.py
|
||||||
|
|||||||
@@ -1,38 +1,53 @@
|
|||||||
# CM4 eMMC provisioning on Proxmox (LXC + host)
|
# 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
|
## What is deployed
|
||||||
|
|
||||||
| Where | What |
|
| 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` |
|
| **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 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) |
|
| **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).
|
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`:
|
1. **LXC** (hostname `cm4-provisioning`, ID = found by hostname or next free):
|
||||||
- Hostname: `cm4-provisioning`
|
|
||||||
- Debian 12, 1 GB RAM, 8 GB rootfs
|
- Debian 12, 1 GB RAM, 8 GB rootfs
|
||||||
- Bind mount: host `/var/lib/cm4-provisioning` → container `/var/lib/cm4-provisioning`
|
- 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**:
|
2. **On the host**:
|
||||||
- `/opt/cm4-provisioning/flash-emmc-on-connect.sh` – flash script
|
- `/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
|
- `/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
|
- `/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)
|
- `/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**:
|
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`):
|
||||||
- Same scripts in `/opt/cm4-provisioning/` and env (for reference/backup)
|
- 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)
|
- 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.
|
|
||||||
|
|
||||||
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
|
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):
|
- **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
|
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:
|
Then connect with `ssh root@<LXC-IP>` (script prints the IP). To get the LXC IP:
|
||||||
`ssh root@10.130.60.224 "pct exec 201 -- hostname -I"`
|
`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:**
|
- **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
|
### 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
|
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)
|
### 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):
|
- **From the LXC** (e.g. after copying the image into the container elsewhere first):
|
||||||
```bash
|
```bash
|
||||||
pct exec 201 -- ls -la /var/lib/cm4-provisioning/
|
pct exec <CTID> -- ls -la /var/lib/cm4-provisioning/
|
||||||
# Copy to that path inside the container; it's the same as the host path.
|
# Replace <CTID> with the ID of the cm4-provisioning container (pct list).
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. Run the provisioning dashboard (optional)
|
### 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`).
|
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
|
```bash
|
||||||
# Copy dashboard into the container (from host, if you have the repo there)
|
# Copy dashboard into the container (from host, if you have the repo there)
|
||||||
# Or from your workstation:
|
# Or from your workstation:
|
||||||
# rsync -a chromium-setup/emmc-provisioning/dashboard/ root@10.130.60.224:/tmp/dashboard/
|
# 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
|
apt-get update && apt-get install -y python3-flask
|
||||||
mkdir -p /opt/cm4-provisioning/dashboard/templates
|
mkdir -p /opt/cm4-provisioning/dashboard/templates
|
||||||
# Copy app.py, templates/index.html, cm4-dashboard.service into the container (see dashboard/README.md)
|
# 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
|
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
|
### 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 |
|
| 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) |
|
| Golden image | `/var/lib/cm4-provisioning/golden.img` (host and LXC see the same file) |
|
||||||
| Flash runs on | Proxmox **host** (udev + rpiboot + dd) |
|
| 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 |
|
| 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
|
#!/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]
|
# Usage: ./deploy-to-proxmox.sh [proxmox_host]
|
||||||
# Example: ./deploy-to-proxmox.sh root@10.130.60.224
|
# Example: ./deploy-to-proxmox.sh root@10.20.30.152
|
||||||
# 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
|
# Optional env:
|
||||||
# Requires: ssh key access to root@<host>
|
# DEPLOY_ROOTFS_STORAGE=name — LXC rootfs storage (default: auto-detect: local-lvm, local, local-zfs, or first active)
|
||||||
# Logging: set DEPLOY_LOG=1 to also write to deploy-YYYYMMDD-HHMMSS.log in the script dir.
|
# 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
|
set -e
|
||||||
PROXMOX="${1:-root@10.130.60.224}"
|
PROXMOX="${1:-root@10.130.60.224}"
|
||||||
@@ -21,24 +32,74 @@ fi
|
|||||||
|
|
||||||
log() { echo "[$(date -Iseconds)] $*"; }
|
log() { echo "[$(date -Iseconds)] $*"; }
|
||||||
|
|
||||||
log "Deploying to $PROXMOX ..."
|
# Optional: gather SSH key and LXC root password for setup inside deploy
|
||||||
# Use a clean staging dir (remove if present so we never write into a symlink or bad state)
|
DEPLOY_SSH_KEY_B64=""
|
||||||
log "[1/4] Cleaning remote staging dir ..."
|
DEPLOY_LXC_PWD_B64=""
|
||||||
ssh "$PROXMOX" "rm -rf /tmp/emmc-provisioning-deploy"
|
if [[ -n "${DEPLOY_LXC_ROOT_PASSWORD:-}" ]]; then
|
||||||
log "[2/4] Rsync repo to $PROXMOX ..."
|
DEPLOY_LXC_PWD_B64=$(echo -n "$DEPLOY_LXC_ROOT_PASSWORD" | base64 -w 0 2>/dev/null || base64 2>/dev/null | tr -d '\n')
|
||||||
rsync -a "$REPO_DIR/" "$PROXMOX:/tmp/emmc-provisioning-deploy/" --exclude='.git' --exclude='scripts/deploy-to-proxmox.sh'
|
fi
|
||||||
log "[3/4] Running remote install (host + LXC) ..."
|
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
|
set -e
|
||||||
DEPLOY=/tmp/emmc-provisioning-deploy
|
DEPLOY=/tmp/emmc-provisioning-deploy
|
||||||
ROOTFS_STORAGE="${ROOTFS_STORAGE:-local-lvm}"
|
|
||||||
BACKUPS_HOST_PATH="${CM4_BACKUPS_HOST_PATH:-}"
|
BACKUPS_HOST_PATH="${CM4_BACKUPS_HOST_PATH:-}"
|
||||||
|
LXC_HOSTNAME="cm4-provisioning"
|
||||||
log() { echo "[$(date -Iseconds)] $*"; }
|
log() { echo "[$(date -Iseconds)] $*"; }
|
||||||
|
|
||||||
# Ensure LXC 201 exists (create if not)
|
# --- Auto-select LXC rootfs storage if not set or not available ---
|
||||||
if ! pct status 201 &>/dev/null; then
|
_storage_exists() { pvesm status 2>/dev/null | awk -v s="$1" 'NR>1 && $1==s && $3=="active" {exit(0)} END{exit(1)}'; }
|
||||||
# Use Debian 12 template: prefer one in cache, else download latest
|
if [[ -n "${ROOTFS_STORAGE:-}" ]] && _storage_exists "$ROOTFS_STORAGE"; then
|
||||||
|
: # use provided and existing storage
|
||||||
|
else
|
||||||
|
ROOTFS_STORAGE=""
|
||||||
|
for cand in local-lvm local local-zfs; do
|
||||||
|
if _storage_exists "$cand"; then
|
||||||
|
ROOTFS_STORAGE="$cand"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ -z "$ROOTFS_STORAGE" ]]; then
|
||||||
|
ROOTFS_STORAGE=$(pvesm status 2>/dev/null | awk 'NR>1 && $3=="active" {print $1; exit}')
|
||||||
|
fi
|
||||||
|
[[ -z "$ROOTFS_STORAGE" ]] && { log "Error: no Proxmox storage found. Run: pvesm status"; exit 1; }
|
||||||
|
log "Using storage: $ROOTFS_STORAGE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 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
|
VZTMPL_DIR=/var/lib/vz/template/cache
|
||||||
DEBIAN12_TMPL=$(ls "$VZTMPL_DIR"/debian-12-standard_*.tar.zst 2>/dev/null | head -1)
|
DEBIAN12_TMPL=$(ls "$VZTMPL_DIR"/debian-12-standard_*.tar.zst 2>/dev/null | head -1)
|
||||||
if [[ -z "$DEBIAN12_TMPL" ]]; then
|
if [[ -z "$DEBIAN12_TMPL" ]]; then
|
||||||
@@ -46,30 +107,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
|
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)
|
DEBIAN12_TMPL=$(ls "$VZTMPL_DIR"/debian-12-standard_*.tar.zst 2>/dev/null | head -1)
|
||||||
fi
|
fi
|
||||||
|
[[ -z "$DEBIAN12_TMPL" ]] && { log "Error: no Debian 12 template found"; exit 1; }
|
||||||
TMPL_NAME=$(basename "$DEBIAN12_TMPL")
|
TMPL_NAME=$(basename "$DEBIAN12_TMPL")
|
||||||
log "Creating LXC 201 (cm4-provisioning) (rootfs on ${ROOTFS_STORAGE}, template ${TMPL_NAME})..."
|
pct create "$CTID" "local:vztmpl/${TMPL_NAME}" \
|
||||||
pct create 201 "local:vztmpl/${TMPL_NAME}" \
|
--hostname "$LXC_HOSTNAME" --memory 1024 --swap 0 --cores 1 \
|
||||||
--hostname cm4-provisioning --memory 1024 --swap 0 --cores 1 \
|
|
||||||
--rootfs "${ROOTFS_STORAGE}:8" --net0 name=eth0,bridge=vmbr0,ip=dhcp \
|
--rootfs "${ROOTFS_STORAGE}:8" --net0 name=eth0,bridge=vmbr0,ip=dhcp \
|
||||||
--unprivileged 0 --features nesting=1 -tag cm4-provisioning
|
--unprivileged 0 --features nesting=1 -tag cm4-provisioning
|
||||||
mkdir -p /var/lib/cm4-provisioning
|
mkdir -p /var/lib/cm4-provisioning
|
||||||
pct set 201 -mp0 /var/lib/cm4-provisioning,mp=/var/lib/cm4-provisioning
|
pct set "$CTID" -mp0 /var/lib/cm4-provisioning,mp=/var/lib/cm4-provisioning
|
||||||
log "LXC 201 created and mount configured."
|
log "LXC $CTID created and mount configured."
|
||||||
else
|
|
||||||
log "LXC 201 already exists."
|
|
||||||
fi
|
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
|
if [[ -n "$BACKUPS_HOST_PATH" ]]; then
|
||||||
mkdir -p "$BACKUPS_HOST_PATH"
|
mkdir -p "$BACKUPS_HOST_PATH"
|
||||||
pct stop 201 2>/dev/null || true
|
pct stop "$CTID" 2>/dev/null || true
|
||||||
pct set 201 -mp1 "$BACKUPS_HOST_PATH",mp=/var/lib/cm4-provisioning/backups
|
pct set "$CTID" -mp1 "$BACKUPS_HOST_PATH",mp=/var/lib/cm4-provisioning/backups
|
||||||
pct start 201 2>/dev/null || true
|
pct start "$CTID" 2>/dev/null || true
|
||||||
log "Backups mount: host $BACKUPS_HOST_PATH -> LXC /var/lib/cm4-provisioning/backups"
|
log "Backups mount: host $BACKUPS_HOST_PATH -> LXC $CTID /var/lib/cm4-provisioning/backups"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Host: install scripts and udev (from host/)
|
# --- Host: scripts, systemd, udev ---
|
||||||
log "Host: installing scripts and udev ..."
|
log "Host: installing scripts and systemd units ..."
|
||||||
mkdir -p /opt/cm4-provisioning /etc/cm4-provisioning
|
mkdir -p /opt/cm4-provisioning /etc/cm4-provisioning
|
||||||
cp "$DEPLOY/host/flash-emmc-on-connect.sh" /opt/cm4-provisioning/
|
cp "$DEPLOY/host/flash-emmc-on-connect.sh" /opt/cm4-provisioning/
|
||||||
chmod +x /opt/cm4-provisioning/flash-emmc-on-connect.sh
|
chmod +x /opt/cm4-provisioning/flash-emmc-on-connect.sh
|
||||||
@@ -77,6 +136,7 @@ cp "$DEPLOY/host/build-cloudinit-image.sh" /opt/cm4-provisioning/
|
|||||||
chmod +x /opt/cm4-provisioning/build-cloudinit-image.sh
|
chmod +x /opt/cm4-provisioning/build-cloudinit-image.sh
|
||||||
cp "$DEPLOY/host/run-shrink-on-host.sh" /opt/cm4-provisioning/
|
cp "$DEPLOY/host/run-shrink-on-host.sh" /opt/cm4-provisioning/
|
||||||
chmod +x /opt/cm4-provisioning/run-shrink-on-host.sh
|
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/
|
cp "$DEPLOY/host/cm4-flash-trigger.sh" /usr/local/bin/
|
||||||
chmod +x /usr/local/bin/cm4-flash-trigger.sh
|
chmod +x /usr/local/bin/cm4-flash-trigger.sh
|
||||||
cp "$DEPLOY/host/cm4-flash.service" /etc/systemd/system/
|
cp "$DEPLOY/host/cm4-flash.service" /etc/systemd/system/
|
||||||
@@ -89,7 +149,8 @@ systemctl enable --now cm4-build-cloudinit.path 2>/dev/null || true
|
|||||||
systemctl enable --now cm4-shrink.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/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/
|
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 ..."
|
log "Host: env and dirs ..."
|
||||||
cat > /opt/cm4-provisioning/env << 'ENV'
|
cat > /opt/cm4-provisioning/env << 'ENV'
|
||||||
GOLDEN_IMAGE=/var/lib/cm4-provisioning/golden.img
|
GOLDEN_IMAGE=/var/lib/cm4-provisioning/golden.img
|
||||||
@@ -101,44 +162,86 @@ touch /etc/cm4-provisioning/enabled
|
|||||||
mkdir -p /var/lib/cm4-provisioning/backups
|
mkdir -p /var/lib/cm4-provisioning/backups
|
||||||
[[ -n "$BACKUPS_HOST_PATH" ]] && mkdir -p "$BACKUPS_HOST_PATH"
|
[[ -n "$BACKUPS_HOST_PATH" ]] && mkdir -p "$BACKUPS_HOST_PATH"
|
||||||
|
|
||||||
# Start LXC if stopped
|
# --- Host: install usbboot (rpiboot) so USB flash/backup works ---
|
||||||
log "Starting LXC 201 if stopped ..."
|
log "Host: installing usbboot (rpiboot)..."
|
||||||
pct start 201 2>/dev/null || true
|
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 ..."
|
log "LXC: installing flash scripts ..."
|
||||||
pct exec 201 -- mkdir -p /opt/cm4-provisioning /etc/cm4-provisioning
|
pct exec "$CTID" -- 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 push "$CTID" "$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 exec "$CTID" -- 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 push "$CTID" "$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 "$CTID" -- 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" -- 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 ..."
|
log "LXC: installing dashboard ..."
|
||||||
pct exec 201 -- mkdir -p /opt/cm4-provisioning/dashboard/templates
|
pct exec "$CTID" -- mkdir -p /opt/cm4-provisioning/dashboard/templates
|
||||||
pct push 201 "$DEPLOY/dashboard/app.py" /opt/cm4-provisioning/dashboard/app.py
|
pct push "$CTID" "$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 "$CTID" "$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 push "$CTID" "$DEPLOY/dashboard/cm4-dashboard.service" /opt/cm4-provisioning/dashboard/cm4-dashboard.service
|
||||||
|
|
||||||
# LXC: install Flask and enable dashboard service
|
# --- LXC: Flask and systemd ---
|
||||||
log "LXC: installing python3-flask and enabling cm4-dashboard service ..."
|
log "LXC: installing python3-flask and enabling cm4-dashboard ..."
|
||||||
pct exec 201 -- bash -c 'apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq python3-flask'
|
pct exec "$CTID" -- 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 "$CTID" -- cp /opt/cm4-provisioning/dashboard/cm4-dashboard.service /etc/systemd/system/
|
||||||
pct exec 201 -- systemctl daemon-reload
|
pct exec "$CTID" -- systemctl daemon-reload
|
||||||
pct exec 201 -- systemctl enable --now cm4-dashboard
|
pct exec "$CTID" -- systemctl enable --now cm4-dashboard
|
||||||
log "LXC: cm4-dashboard enabled and started."
|
log "LXC: cm4-dashboard enabled and started."
|
||||||
|
|
||||||
log "Deploy done (remote)."
|
# --- LXC: optional SSH (root password + SSH key from deploy env) ---
|
||||||
echo "Next: Install usbboot on host when online: ssh <host> 'bash /tmp/emmc-provisioning-deploy/scripts/install-usbboot-on-host.sh'"
|
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
|
REMOTE
|
||||||
|
|
||||||
log "[4/4] Deploy finished."
|
log "[5/5] Deploy finished."
|
||||||
echo ""
|
echo ""
|
||||||
echo "Done. Put golden.img in /var/lib/cm4-provisioning/ on the host (or scp to LXC 201 at /var/lib/cm4-provisioning/)."
|
echo "=== Deploy complete ==="
|
||||||
[[ -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 "Host and LXC are fully set up: usbboot (rpiboot), PiShrink, dashboard, systemd, udev."
|
||||||
echo "When the host has internet, run on the host: bash /tmp/emmc-provisioning-deploy/scripts/install-usbboot-on-host.sh"
|
echo ""
|
||||||
echo "Dashboard: install flask in LXC 201 and enable cm4-dashboard.service (see docs/PROXMOX-LXC-DEPLOYMENT.md)."
|
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
|
if [[ -n "$LOG_FILE" ]]; then
|
||||||
echo "Log written to: $LOG_FILE"
|
echo " - Log: $LOG_FILE"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -18,3 +18,13 @@ for dir in mass-storage-gadget64 mass-storage-gadget; do
|
|||||||
[[ -d "$dir" ]] && cp -a "$dir" /opt/usbboot/
|
[[ -d "$dir" ]] && cp -a "$dir" /opt/usbboot/
|
||||||
done
|
done
|
||||||
echo "usbboot installed at /opt/usbboot/rpiboot"
|
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
|
#!/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:
|
# Usage:
|
||||||
# ./setup-lxc-ssh.sh [proxmox_host] [ssh_public_key_file]
|
# ./setup-lxc-ssh.sh [proxmox_host] [ssh_public_key_file]
|
||||||
# ROOT_PASSWORD='yourpassword' ./setup-lxc-ssh.sh [proxmox_host] [ssh_public_key_file]
|
# ROOT_PASSWORD='yourpassword' ./setup-lxc-ssh.sh [proxmox_host] [ssh_public_key_file]
|
||||||
#
|
# CTID=202 ./setup-lxc-ssh.sh root@10.130.60.224 # force a specific container ID
|
||||||
# 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
|
|
||||||
#
|
#
|
||||||
# If ssh_public_key_file is omitted, uses ~/.ssh/id_ed25519.pub or ~/.ssh/id_rsa.pub.
|
# If ssh_public_key_file is omitted, uses ~/.ssh/id_ed25519.pub or ~/.ssh/id_rsa.pub.
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
PROXMOX="${1:-root@10.130.60.224}"
|
PROXMOX="${1:-root@10.130.60.224}"
|
||||||
KEY_FILE="${2:-}"
|
KEY_FILE="${2:-}"
|
||||||
CTID="${CTID:-201}"
|
CTID="${CTID:-}"
|
||||||
|
|
||||||
# Find public key
|
# Find public key
|
||||||
if [[ -z "$KEY_FILE" ]]; then
|
if [[ -z "$KEY_FILE" ]]; then
|
||||||
@@ -34,39 +31,45 @@ KEY_CONTENT=$(cat "$KEY_FILE")
|
|||||||
ROOT_PASSWORD="${ROOT_PASSWORD:-}"
|
ROOT_PASSWORD="${ROOT_PASSWORD:-}"
|
||||||
|
|
||||||
echo "Using key from: $KEY_FILE"
|
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
|
set -e
|
||||||
CTID="$CTID"
|
# Resolve CTID by hostname if not provided
|
||||||
KEY_CONTENT='$(echo "$KEY_CONTENT" | sed "s/'/'\\\\''/g")'
|
if [[ -z "$CTID" ]]; then
|
||||||
ROOT_PASSWORD='$(echo "$ROOT_PASSWORD" | sed "s/'/'\\\\''/g")'
|
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
|
# Ensure container is running
|
||||||
pct start \$CTID 2>/dev/null || true
|
pct start $CTID 2>/dev/null || true
|
||||||
sleep 2
|
sleep 2
|
||||||
|
|
||||||
# Install openssh-server if missing, enable and start
|
# 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
|
# 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
|
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
|
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
|
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)
|
# Set root password if provided (pass via stdin so no quoting in -c)
|
||||||
if [[ -n "\$ROOT_PASSWORD" ]]; then
|
if [[ -n "$ROOT_PASSWORD" ]]; then
|
||||||
echo "root:\$ROOT_PASSWORD" | pct exec \$CTID -- chpasswd
|
echo "root:$ROOT_PASSWORD" | pct exec $CTID -- chpasswd
|
||||||
echo "Root password set."
|
echo "Root password set."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Add SSH key to root (pass key via stdin to avoid quoting issues)
|
# 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"
|
echo "SSH key added to /root/.ssh/authorized_keys"
|
||||||
|
|
||||||
# Show IP for convenience
|
# Show IP for convenience
|
||||||
IP=\$(pct exec \$CTID -- hostname -I 2>/dev/null | awk '{print \$1}')
|
IP=$(pct exec $CTID -- hostname -I 2>/dev/null | awk '{print $1}')
|
||||||
echo "Done. Connect with: ssh root@\$IP"
|
echo "Done. Connect with: ssh root@$IP"
|
||||||
REMOTE
|
REMOTE
|
||||||
|
|||||||
Reference in New Issue
Block a user