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:
nearxos
2026-02-19 11:59:25 +02:00
parent a3661df8c2
commit 5afb194daf
5 changed files with 223 additions and 108 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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