Fix Proxmox VM login — force user creation via runcmd

The Arch Linux cloud image ignores Proxmox's --ciuser/--cipassword
and the cloud-init chpasswd module depending on version. The previous
approach had three conflicting methods fighting each other.

Fixed by:
- Removed --ciuser/--cipassword from qm set (they conflict with snippet)
- Removed chpasswd cloud-init module (unreliable on Arch)
- Set users: [] to disable cloud-init's default user module
- ALL user setup now done via runcmd (runs as root, always works):
  - Sets root password to 'darkforge' as fallback
  - Creates darkforge user via useradd + chpasswd
  - Grants passwordless sudo via /etc/sudoers.d/
  - Enables PermitRootLogin yes as safety net
- Package install via explicit pacman commands instead of packages: module
  (Arch cloud-init packages module can be unreliable)
- Added pacman-key --init/--populate before package install

Login credentials:
  user: darkforge  password: darkforge
  user: root       password: darkforge  (fallback)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 15:48:24 +01:00
parent 6d1b87c7ab
commit fe6ee25d1c

View File

@@ -108,8 +108,9 @@ qm set "${VMID}" --scsihw virtio-scsi-single
echo ">>> Configuring cloud-init..."
qm set "${VMID}" --ide2 "${STORAGE}:cloudinit"
qm set "${VMID}" --ciuser "darkforge"
qm set "${VMID}" --cipassword "darkforge"
# Don't use --ciuser/--cipassword — they conflict with the snippet on Arch.
# We handle ALL user creation in the cloud-init snippet runcmd instead.
qm set "${VMID}" --ipconfig0 "ip=dhcp"
# Enable nested virtualization (for QEMU-in-QEMU boot tests)
@@ -120,67 +121,64 @@ echo ">>> Resizing disk to ${DISK_SIZE}..."
qm resize "${VMID}" scsi0 "${DISK_SIZE}" 2>/dev/null || true
# --- Generate cloud-init user-data snippet -----------------------------------
# This runs on first boot inside the VM
# NOTE: The Arch Linux cloud image has quirks with user/password handling.
# We do EVERYTHING via runcmd to guarantee it works regardless of cloud-init
# version or Proxmox's cloud-init integration behavior.
SNIPPET_DIR="/var/lib/vz/snippets"
mkdir -p "${SNIPPET_DIR}"
cat > "${SNIPPET_DIR}/darkforge-test-init.yaml" << 'CLOUDINIT'
#cloud-config
# Enable SSH password authentication (cloud images disable it by default)
ssh_pwauth: true
chpasswd:
expire: false
users:
- name: darkforge
password: darkforge
type: text
# Disable the default user module to avoid conflicts
# We create our user manually via runcmd below
users: []
ssh_pwauth: true
# Ensure sshd allows password auth
write_files:
- path: /etc/ssh/sshd_config.d/99-darkforge.conf
content: |
PasswordAuthentication yes
PermitRootLogin no
package_update: true
packages:
- base-devel
- git
- wget
- curl
- rust
- cargo
- qemu-full
- edk2-ovmf
- squashfs-tools
- xorriso
- dosfstools
- mtools
- python
- bc
- rsync
- openssh
- tmux
PermitRootLogin yes
runcmd:
# Restart sshd to pick up the password auth config
# --- USER SETUP (do this first, before anything else) ----------------------
# Set root password so we always have a fallback login
- echo 'root:darkforge' | chpasswd
# Create the darkforge user if it doesn't exist
- id darkforge &>/dev/null || useradd -m -G wheel -s /bin/bash darkforge
- echo 'darkforge:darkforge' | chpasswd
# Give darkforge sudo/wheel access without password
- echo 'darkforge ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/darkforge
- chmod 440 /etc/sudoers.d/darkforge
# Enable and restart sshd
- systemctl enable sshd
- systemctl restart sshd
# Grow the partition to fill the disk
- growpart /dev/sda 2 || true
- resize2fs /dev/sda2 || btrfs filesystem resize max / || true
# --- DISK RESIZE -----------------------------------------------------------
- growpart /dev/sda 2 || growpart /dev/vda 2 || true
- resize2fs /dev/sda2 || resize2fs /dev/vda2 || btrfs filesystem resize max / || true
# Clone the DarkForge project
# --- PACKAGE INSTALL -------------------------------------------------------
- pacman-key --init
- pacman-key --populate archlinux
- pacman -Syu --noconfirm
- pacman -S --noconfirm --needed base-devel git wget curl rust cargo qemu-full edk2-ovmf squashfs-tools xorriso dosfstools mtools python bc rsync openssh tmux
# --- CLONE PROJECT ---------------------------------------------------------
- |
su - darkforge -c '
cd /home/darkforge
git clone --recurse-submodules https://git.dannyhaslund.dk/danny8632/darkforge.git 2>/dev/null || \
git clone --recurse-submodules https://github.com/danny8632/darkforge.git 2>/dev/null || \
echo "CLONE FAILED — manually clone the repo"
echo "CLONE FAILED — manually clone the repo after login"
'
# Create the darkforge-test convenience command
# --- INSTALL CONVENIENCE COMMAND -------------------------------------------
- |
cat > /usr/local/bin/darkforge-test << 'DTEOF'
#!/bin/bash
@@ -197,7 +195,7 @@ runcmd:
DTEOF
chmod +x /usr/local/bin/darkforge-test
# Signal that provisioning is done
# --- SIGNAL DONE -----------------------------------------------------------
- touch /home/darkforge/.provisioned
- chown darkforge:darkforge /home/darkforge/.provisioned
CLOUDINIT