Update provisioning documentation and scripts for improved Proxmox deployment</message>
<message>Add a new step-by-step guide for deploying the CM4 eMMC provisioning service on a new Proxmox instance, enhancing clarity for users. Update existing documentation to reflect changes in network configuration options, including the introduction of LAN subnet settings for DHCP and TFTP. Modify cloud-init scripts to ensure proper management of DNS settings and improve the handling of network interfaces. Additionally, enhance the toggle script for network boot to dynamically read the LAN gateway from configuration files, streamlining the setup process and improving user experience.
This commit is contained in:
@@ -23,6 +23,12 @@
|
||||
# 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
|
||||
# DEPLOY_LXC_WAN_BRIDGE=vmbr0 — Proxmox bridge for WAN (eth0); default vmbr0
|
||||
# DEPLOY_LXC_WAN_IP=dhcp — WAN address: dhcp (default) or static e.g. 192.168.1.10/24
|
||||
# DEPLOY_LXC_LAN_BRIDGE=vmbr1 — If set, add eth1 as LAN on this bridge (e.g. provisioning / network-boot)
|
||||
# DEPLOY_LXC_LAN_SUBNET=10.20.50.1/24 — LXC IP on LAN (gateway); used only if DEPLOY_LXC_LAN_BRIDGE is set; default 10.20.50.1/24
|
||||
#
|
||||
# Legacy: DEPLOY_LXC_NET1="name=eth1,bridge=vmbr1,ip=10.20.50.1/24" still works; overridden by DEPLOY_LXC_LAN_BRIDGE + DEPLOY_LXC_LAN_SUBNET if both are set.
|
||||
#
|
||||
# Requires: ssh key access to root@<host>. For full install (usbboot, PiShrink), host needs internet.
|
||||
|
||||
@@ -144,8 +150,8 @@ rsync -a "$REPO_DIR/" "$PROXMOX:/tmp/emmc-provisioning-deploy/" --exclude='.git'
|
||||
|
||||
log "[4/5] Running remote install (host + LXC) ..."
|
||||
|
||||
# Pass optional LXC SSH vars (base64) and selected storage
|
||||
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'
|
||||
# Pass optional LXC SSH vars (base64), selected storage, and network (WAN/LAN bridge + subnet)
|
||||
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:-}' DEPLOY_LXC_WAN_BRIDGE='${DEPLOY_LXC_WAN_BRIDGE:-}' DEPLOY_LXC_WAN_IP='${DEPLOY_LXC_WAN_IP:-}' DEPLOY_LXC_LAN_BRIDGE='${DEPLOY_LXC_LAN_BRIDGE:-}' DEPLOY_LXC_LAN_SUBNET='${DEPLOY_LXC_LAN_SUBNET:-}' DEPLOY_LXC_NET1='${DEPLOY_LXC_NET1:-}'" bash -s << 'REMOTE'
|
||||
set -e
|
||||
DEPLOY=/tmp/emmc-provisioning-deploy
|
||||
ROOTFS_STORAGE="${ROOTFS_STORAGE:?ROOTFS_STORAGE not set}"
|
||||
@@ -185,14 +191,24 @@ else
|
||||
fi
|
||||
[[ -z "$DEBIAN12_TMPL" ]] && { log "Error: no Debian 12 template found"; exit 1; }
|
||||
TMPL_NAME=$(basename "$DEBIAN12_TMPL")
|
||||
# Optional: add eth1 for network-boot LAN (DHCP+TFTP). Set DEPLOY_LXC_NET1 e.g. "name=eth1,bridge=vmbr1,ip=10.20.50.1/24"
|
||||
# WAN (eth0): bridge and IP from env; default vmbr0 + dhcp
|
||||
WAN_BRIDGE="${DEPLOY_LXC_WAN_BRIDGE:-vmbr0}"
|
||||
WAN_IP="${DEPLOY_LXC_WAN_IP:-dhcp}"
|
||||
# LAN (eth1): optional; use DEPLOY_LXC_LAN_BRIDGE + DEPLOY_LXC_LAN_SUBNET, or legacy DEPLOY_LXC_NET1
|
||||
NET1_OPT=""
|
||||
if [[ -n "${DEPLOY_LXC_NET1:-}" ]]; then
|
||||
if [[ -n "${DEPLOY_LXC_LAN_BRIDGE:-}" ]]; then
|
||||
LAN_SUBNET="${DEPLOY_LXC_LAN_SUBNET:-10.20.50.1/24}"
|
||||
NET1_OPT="--net1 name=eth1,bridge=${DEPLOY_LXC_LAN_BRIDGE},ip=${LAN_SUBNET}"
|
||||
log "LXC network: eth0 WAN bridge=$WAN_BRIDGE ip=$WAN_IP; eth1 LAN bridge=$DEPLOY_LXC_LAN_BRIDGE ip=$LAN_SUBNET"
|
||||
elif [[ -n "${DEPLOY_LXC_NET1:-}" ]]; then
|
||||
NET1_OPT="--net1 $DEPLOY_LXC_NET1"
|
||||
log "LXC network: eth0 WAN bridge=$WAN_BRIDGE ip=$WAN_IP; eth1 from DEPLOY_LXC_NET1"
|
||||
else
|
||||
log "LXC network: eth0 WAN bridge=$WAN_BRIDGE ip=$WAN_IP (no LAN interface)"
|
||||
fi
|
||||
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 $NET1_OPT \
|
||||
--rootfs "${ROOTFS_STORAGE}:8" --net0 name=eth0,bridge="$WAN_BRIDGE",ip="$WAN_IP" $NET1_OPT \
|
||||
--unprivileged 0 --features nesting=1 -tag cm4-provisioning
|
||||
mkdir -p /var/lib/cm4-provisioning
|
||||
pct set "$CTID" -mp0 /var/lib/cm4-provisioning,mp=/var/lib/cm4-provisioning
|
||||
@@ -302,6 +318,24 @@ fi
|
||||
log "Starting LXC $CTID if stopped ..."
|
||||
pct start "$CTID" 2>/dev/null || true
|
||||
|
||||
# --- LXC: write lan-subnet.conf when LAN bridge/subnet is set (so dnsmasq/NAT/toggle use same subnet) ---
|
||||
LAN_SUBNET_FOR_CONF="${DEPLOY_LXC_LAN_SUBNET:-}"
|
||||
[[ -z "$LAN_SUBNET_FOR_CONF" && -n "${DEPLOY_LXC_LAN_BRIDGE:-}" ]] && LAN_SUBNET_FOR_CONF="10.20.50.1/24"
|
||||
if [[ -n "$LAN_SUBNET_FOR_CONF" ]]; then
|
||||
if [[ "$LAN_SUBNET_FOR_CONF" =~ ^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/([0-9]+)$ ]]; then
|
||||
LAN_GW="${BASH_REMATCH[1]}"
|
||||
PREFIX="${BASH_REMATCH[2]}"
|
||||
BASE_3="${LAN_GW%.*}"
|
||||
LAN_CIDR="${BASE_3}.0/${PREFIX}"
|
||||
DHCP_RANGE_START="${BASE_3}.100"
|
||||
DHCP_RANGE_END="${BASE_3}.200"
|
||||
pct exec "$CTID" -- bash -c "mkdir -p /opt/cm4-provisioning && echo 'LAN_GW=$LAN_GW' > /opt/cm4-provisioning/lan-subnet.conf && echo 'LAN_CIDR=$LAN_CIDR' >> /opt/cm4-provisioning/lan-subnet.conf && echo 'DHCP_RANGE_START=$DHCP_RANGE_START' >> /opt/cm4-provisioning/lan-subnet.conf && echo 'DHCP_RANGE_END=$DHCP_RANGE_END' >> /opt/cm4-provisioning/lan-subnet.conf"
|
||||
log "LXC: wrote /opt/cm4-provisioning/lan-subnet.conf (LAN_GW=$LAN_GW, LAN_CIDR=$LAN_CIDR, DHCP ${DHCP_RANGE_START}-${DHCP_RANGE_END})"
|
||||
else
|
||||
log "Warning: DEPLOY_LXC_LAN_SUBNET=$LAN_SUBNET_FOR_CONF not in form A.B.C.D/PREFIX; skipping lan-subnet.conf"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- LXC: flash scripts (for reference; actual flash runs on host) ---
|
||||
log "LXC: installing flash scripts ..."
|
||||
pct exec "$CTID" -- mkdir -p /opt/cm4-provisioning /etc/cm4-provisioning
|
||||
|
||||
130
emmc-provisioning/scripts/edit-cloudinit-on-image.sh
Executable file
130
emmc-provisioning/scripts/edit-cloudinit-on-image.sh
Executable file
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env bash
|
||||
# Mount a Raspberry Pi OS .img or .img.xz, edit cloud-init NoCloud files on the boot
|
||||
# partition, then unmount and (if .img.xz) recompress. Requires sudo for loop/mount.
|
||||
#
|
||||
# Usage:
|
||||
# ./edit-cloudinit-on-image.sh <path-to-image.img.xz>
|
||||
# ./edit-cloudinit-on-image.sh <path-to-image.img>
|
||||
#
|
||||
# Options:
|
||||
# --no-recompress If image was .img.xz, leave decompressed .img and do not overwrite
|
||||
# --replace-with-repo Copy user-data, meta-data, network-config from repo before editing
|
||||
#
|
||||
# Example:
|
||||
# ./edit-cloudinit-on-image.sh /path/to/gnss-bootstrap-20260223-215010.img.xz
|
||||
#
|
||||
# Backup the image before running if you want to keep the original.
|
||||
|
||||
set -e
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CLOUDINIT_SRC="$REPO_ROOT/emmc-provisioning/cloud-init"
|
||||
NO_RECOMPRESS=""
|
||||
REPLACE_WITH_REPO=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--no-recompress) NO_RECOMPRESS=1; shift ;;
|
||||
--replace-with-repo) REPLACE_WITH_REPO=1; shift ;;
|
||||
-*) echo "Unknown option: $1"; exit 1 ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
IMAGE_IN="$1"
|
||||
[[ -n "$IMAGE_IN" && -f "$IMAGE_IN" ]] || {
|
||||
echo "Usage: $0 [--no-recompress] [--replace-with-repo] <path-to-image.img.xz|.img>"
|
||||
echo "Example: $0 gnss-bootstrap-20260223-215010.img.xz"
|
||||
exit 1
|
||||
}
|
||||
|
||||
IMAGE_IN="$(realpath "$IMAGE_IN")"
|
||||
WORK_DIR=""
|
||||
IMG_FILE=""
|
||||
ORIGINAL_XZ=""
|
||||
cleanup() {
|
||||
if [[ -n "$MNT" && -d "$MNT" ]]; then
|
||||
sudo umount "$MNT" 2>/dev/null || true
|
||||
fi
|
||||
if [[ -n "$LOOP" && -b "$LOOP" ]]; then
|
||||
sudo losetup -d "$LOOP" 2>/dev/null || true
|
||||
fi
|
||||
if [[ -n "$WORK_DIR" && -d "$WORK_DIR" ]]; then
|
||||
if [[ -n "$NO_RECOMPRESS" && -n "$ORIGINAL_XZ" ]]; then
|
||||
echo "Work dir left at: $WORK_DIR"
|
||||
else
|
||||
rm -rf "$WORK_DIR"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [[ "$IMAGE_IN" == *.img.xz ]]; then
|
||||
ORIGINAL_XZ="$IMAGE_IN"
|
||||
echo "Decompressing $(basename "$ORIGINAL_XZ") (this may take a minute and needs ~3GB free)…"
|
||||
WORK_DIR=$(mktemp -d -p "${TMPDIR:-/tmp}" edit-cloudinit.XXXXXX)
|
||||
IMG_FILE="$WORK_DIR/image.img"
|
||||
xz -T 0 -d -k -f -c "$ORIGINAL_XZ" > "$IMG_FILE"
|
||||
else
|
||||
WORK_DIR=$(mktemp -d -p "${TMPDIR:-/tmp}" edit-cloudinit.XXXXXX)
|
||||
IMG_FILE="$IMAGE_IN"
|
||||
fi
|
||||
|
||||
echo "Attaching image and mounting boot partition…"
|
||||
LOOP=$(sudo losetup -f --show -P "$IMG_FILE")
|
||||
# Force kernel to scan partition table so /dev/loopNp1, p2 etc. appear
|
||||
sudo partprobe "$LOOP" 2>/dev/null || true
|
||||
sleep 0.5
|
||||
boot_part="${LOOP}p1"
|
||||
[[ -b "$boot_part" ]] || boot_part="${LOOP}p2"
|
||||
[[ -b "$boot_part" ]] || {
|
||||
echo "Boot partition not found on image. Partitions on image:"
|
||||
ls -la "${LOOP}"p* 2>/dev/null || true
|
||||
sudo fdisk -l "$IMG_FILE" 2>/dev/null || true
|
||||
exit 1
|
||||
}
|
||||
|
||||
MNT="$WORK_DIR/mnt"
|
||||
mkdir -p "$MNT"
|
||||
sudo mount "$boot_part" "$MNT"
|
||||
|
||||
if [[ -n "$REPLACE_WITH_REPO" && -d "$CLOUDINIT_SRC" ]]; then
|
||||
echo "Copying cloud-init files from repo into boot partition…"
|
||||
for f in user-data meta-data network-config; do
|
||||
if [[ -f "$CLOUDINIT_SRC/$f" ]]; then
|
||||
sudo cp "$CLOUDINIT_SRC/$f" "$MNT/$f"
|
||||
elif [[ -f "$CLOUDINIT_SRC/$f.bootstrap" ]]; then
|
||||
sudo cp "$CLOUDINIT_SRC/$f.bootstrap" "$MNT/$f"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Boot partition is mounted at: $MNT"
|
||||
echo "Cloud-init files to edit:"
|
||||
echo " $MNT/user-data"
|
||||
echo " $MNT/meta-data"
|
||||
echo " $MNT/network-config"
|
||||
echo ""
|
||||
EDITOR="${EDITOR:-nano}"
|
||||
echo "Opening editor ($EDITOR). Save and exit when done."
|
||||
read -r -p "Press Enter to open $EDITOR on these files…"
|
||||
sudo "$EDITOR" "$MNT/user-data" "$MNT/meta-data" "$MNT/network-config"
|
||||
|
||||
echo "Unmounting…"
|
||||
sudo umount "$MNT"
|
||||
MNT=""
|
||||
sudo losetup -d "$LOOP"
|
||||
LOOP=""
|
||||
|
||||
if [[ -n "$ORIGINAL_XZ" && -z "$NO_RECOMPRESS" ]]; then
|
||||
echo "Recompressing to $(basename "$ORIGINAL_XZ")…"
|
||||
xz -T 0 -z -f -k "$IMG_FILE"
|
||||
mv -f "${IMG_FILE}.xz" "$ORIGINAL_XZ"
|
||||
echo "Done. Updated: $ORIGINAL_XZ"
|
||||
rm -rf "$WORK_DIR"
|
||||
WORK_DIR=""
|
||||
elif [[ -n "$NO_RECOMPRESS" && -n "$ORIGINAL_XZ" ]]; then
|
||||
echo "Left decompressed image at: $IMG_FILE"
|
||||
echo "Recompress manually: xz -z -k \"$IMG_FILE\""
|
||||
fi
|
||||
@@ -25,7 +25,17 @@ if [[ -n "$TARGET" ]]; then
|
||||
fi
|
||||
|
||||
# --- Running inside the LXC from here ---
|
||||
echo "Configuring network boot (DHCP + TFTP on eth1, NAT via eth0) ..."
|
||||
# LAN subnet: use /opt/cm4-provisioning/lan-subnet.conf (written by deploy-to-proxmox.sh when DEPLOY_LXC_LAN_SUBNET is set)
|
||||
LAN_CONF="/opt/cm4-provisioning/lan-subnet.conf"
|
||||
if [[ -f "$LAN_CONF" ]]; then
|
||||
source "$LAN_CONF"
|
||||
else
|
||||
LAN_GW="10.20.50.1"
|
||||
LAN_CIDR="10.20.50.0/24"
|
||||
DHCP_RANGE_START="10.20.50.100"
|
||||
DHCP_RANGE_END="10.20.50.200"
|
||||
fi
|
||||
echo "Configuring network boot (DHCP + TFTP on eth1, NAT via eth0) — LAN $LAN_CIDR (gateway $LAN_GW), DHCP ${DHCP_RANGE_START}-${DHCP_RANGE_END} ..."
|
||||
|
||||
# 1) Install dnsmasq
|
||||
if ! command -v dnsmasq >/dev/null 2>&1; then
|
||||
@@ -34,12 +44,12 @@ fi
|
||||
|
||||
# 2) dnsmasq config for eth1 only (DHCP + TFTP); PXE options in network-boot-pxe.conf (toggle with toggle-network-boot-dhcp.sh)
|
||||
mkdir -p /etc/dnsmasq.d
|
||||
cat > /etc/dnsmasq.d/network-boot.conf << 'DNSMASQ'
|
||||
cat > /etc/dnsmasq.d/network-boot.conf << DNSMASQ
|
||||
# DHCP on eth1 only (provisioning LAN)
|
||||
# TFTP and PXE options are in network-boot-pxe.conf, controlled by toggle-network-boot-dhcp.sh
|
||||
interface=eth1
|
||||
bind-interfaces
|
||||
dhcp-range=10.20.50.100,10.20.50.200,12h
|
||||
dhcp-range=${DHCP_RANGE_START},${DHCP_RANGE_END},12h
|
||||
log-dhcp
|
||||
log-queries
|
||||
port=0
|
||||
@@ -89,14 +99,14 @@ fi
|
||||
echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-cm4-network-boot.conf
|
||||
sysctl -p /etc/sysctl.d/99-cm4-network-boot.conf 2>/dev/null || sysctl -w net.ipv4.ip_forward=1
|
||||
|
||||
# 5) NAT: 10.20.50.0/24 -> eth0 (masquerade)
|
||||
# 5) NAT: LAN subnet -> eth0 (masquerade)
|
||||
if command -v nft >/dev/null 2>&1; then
|
||||
mkdir -p /etc/nftables.d
|
||||
cat > /etc/nftables.d/nat-lan.conf << 'NFT'
|
||||
cat > /etc/nftables.d/nat-lan.conf << NFT
|
||||
table ip nat {
|
||||
chain postrouting {
|
||||
type nat hook postrouting priority srcnat; policy accept;
|
||||
ip saddr 10.20.50.0/24 oifname "eth0" masquerade
|
||||
ip saddr ${LAN_CIDR} oifname "eth0" masquerade
|
||||
}
|
||||
}
|
||||
NFT
|
||||
@@ -110,8 +120,8 @@ NFT
|
||||
echo "NAT rule added (nftables) and saved to /etc/nftables.d/nat-lan.conf"
|
||||
else
|
||||
# Fallback iptables
|
||||
iptables -t nat -C POSTROUTING -s 10.20.50.0/24 -o eth0 -j MASQUERADE 2>/dev/null || \
|
||||
iptables -t nat -A POSTROUTING -s 10.20.50.0/24 -o eth0 -j MASQUERADE
|
||||
iptables -t nat -C POSTROUTING -s "${LAN_CIDR}" -o eth0 -j MASQUERADE 2>/dev/null || \
|
||||
iptables -t nat -A POSTROUTING -s "${LAN_CIDR}" -o eth0 -j MASQUERADE
|
||||
echo "NAT rule added (iptables)."
|
||||
fi
|
||||
|
||||
@@ -120,6 +130,6 @@ systemctl enable dnsmasq
|
||||
systemctl restart dnsmasq
|
||||
|
||||
echo "Network boot setup done."
|
||||
echo " - DHCP + TFTP on eth1 (10.20.50.1), range 10.20.50.100-200"
|
||||
echo " - NAT: 10.20.50.0/24 -> eth0 (internet)"
|
||||
echo " - DHCP + TFTP on eth1 ($LAN_GW), range ${DHCP_RANGE_START}-${DHCP_RANGE_END}"
|
||||
echo " - NAT: ${LAN_CIDR} -> eth0 (internet)"
|
||||
echo " - TFTP root: /srv/tftpboot (RPi boot files; initrd.img if provided)"
|
||||
|
||||
Reference in New Issue
Block a user